Installation

  1. Install R
  2. Install RTools if you are on Windows
  3. Install RStudio

R and RMarkdown in RStudio (version 2023.06.1+524) was used to generate this document:

R version 4.3.1 (2023-06-16)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.6.3

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/Toronto
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] digest_0.6.33     R6_2.5.1          fastmap_1.1.1     xfun_0.39        
 [5] cachem_1.0.8      knitr_1.43        htmltools_0.5.7   rmarkdown_2.23   
 [9] cli_3.6.1         sass_0.4.7        jquerylib_0.1.4   compiler_4.3.1   
[13] rstudioapi_0.15.0 tools_4.3.1       evaluate_0.21     bslib_0.5.0      
[17] yaml_2.3.7        rlang_1.1.1       jsonlite_1.8.7   

Libraries

Install R libraries if needed.

install.packages("rmarkdown")
install.packages("bookdown")
install.packages("knitr")
install.packages("tidyverse")
install.packages("glue")
install.packages("readxl")
install.packages("ggtext")
install.packages("scales")
install.packages("patchwork")
install.packages("DiagrammeR")
install.packages("DiagrammeRsvg")
install.packages("webshot2")
install.packages("magick")
install.packages("rsvg")
install.packages("sf")

Load R libraries.

library(DiagrammeR)
library(DiagrammeRsvg)
library(ggtext)
library(glue)
library(patchwork)
library(readxl)
library(rsvg)
library(sf)
library(tidyverse)

Settings

settings <- list()

# Infrastructure types in order
settings$type_recode_infra <- c(
    PBL = "Cycle Track",
    BUF = "Buffered Lane",
    PL = "Painted Lane",
    LSB = "Local Street\nBikeway"
)

# Infrastructure types to remove
settings$type_filter_infra <- c("N", "None", "SR")

# Road types in order
settings$type_recode_road <- c(
    Arterial = "Arterial",
    Collector = "Collector",
    Local = "Local"
)

# Column references
settings$year_col_road <- "install_year"
settings$type_col_road <- "road_type"
settings$type_col_infra <- "infra_type"

# Set years of interest
settings$year_min <- 2009
settings$year_max <- 2022

# Plot settings
settings$line_year <- 2019

Functions

Function 1: Calculate Yearly Road Length

The following function calculates yearly road lengths by infrastructure type using cumulative sums and filling in missing years and types.

For a given infrastructure type, the total road length for a given year is expressed below:

\[ length_{year,type} = f(year,type) = \sum_{i=year_{min}}^{year}l_{i, type} \]

Where:

  • \(year\) is the given year
  • \(type\) is the infrastructure type
  • \(year_{min}\) is the earliest year available in the data
  • \(l_{i,type}\) is the road length \(l\) for previous years \(i\) and infrastructure \(j\)
  • \(l_{i,type}\) is set to 0 if there is no data
#' Calculate Yearly Road Lengths By Infrastructure Type
#' 
#' Calculates the cumulative yearly road lengths by infrastructure type without considering infrastructure changes.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param year_col The name (char) or index (int) of the column containing the years.
#' @param type_col The name (char) or index (int) of the column containing the infrastructure type
#' @param len_col The name (char) or index (int) of the column containing the road lengths.
#' @param out_col The name (char) of the column containing the calculated yearly road lengths by type.
#'
#' @return A data.frame with three columns containing the year, type, and calculated yearly road lengths by type.
#' @export
#'
calc_yearly_len <- function(
        df,
        year_col = "install_year",
        type_col = "install_type",
        len_col = "segment_len",
        out_col = "len",
        year_min = settings$year_min,
        year_max = settings$year_max
    ) {
    
    # Convert data types
    df[[year_col]] <- as.integer(df[[year_col]])
    df[[type_col]] <- as.character(df[[type_col]])
    df[[len_col]] <- as.numeric(df[[len_col]])
    
    # Remove rows with empty type
    out <- df %>% filter(
        !is.na(.data[[type_col]])
    )
    
    # Filter to min and max years
    if (year_min > 0) {
        df <- df %>% filter(
            .data[[year_col]] >= year_min
        )
    } else {
        year_min <- min(out[[year_col]], na.rm = TRUE)
    }
    if (year_max > 0) {
        df <- df %>% filter(
            .data[[year_col]] <= year_max
        )
    } else {
        year_max <- max(out[[year_col]], na.rm = TRUE)
    }
    
    # Add dummy len for each type and year combo
    # Covers cases where type and year combo does not exist
    # E.g. No new PL installs in 2021, hence a record PL in 2021 does not exist
    type_uniq <- unique(out[[type_col]])
    type_n <- length(type_uniq)
    year_uniq <- year_min:year_max
    year_n <- length(year_uniq)
    out <- out %>% add_row(
        !!year_col := rep(year_uniq, each = type_n),
        !!type_col := rep(type_uniq, year_n),
        !!len_col := rep(0, type_n * year_n)
    )
    
    # Calc cumsum for each non-empty type ordered by year
    out <- out %>%
        arrange(.data[[year_col]]) %>%
        group_by(.data[[type_col]]) %>%
        mutate(
            !!out_col := cumsum(.data[[len_col]])
        )

    # Get the last cumsum for each year and type
    out <- out %>%
        group_by(.data[[year_col]], .data[[type_col]]) %>%
        arrange(desc(row_number())) %>%
        slice(1)
    
    # Return only the columns spec
    out <- out %>% select(c(
            year_col,
            type_col,
            out_col
        ))
    return(out)
}

Function 2: Calculate Yearly Adjusted Road Length

The following function calculates yearly adjusted road lengths by infrastructure type using cumulative sums and filling in missing years and types.

For a given infrastructure type, the total adjusted road length for a given year is expressed below:

\[ length_{year,type}^{install} + length_{year,type}^{change_i} - length_{year,type}^{replacement_i} \] Where:

  • \(length_{year,type}^{install}\) are the yearly cumulative road lengths for an infrastructure \(type\) installation
  • \(length_{year,type}^{change_i}\) are the yearly cumulative road lengths for an infrastructure \(type\) change in order \(i\)
  • \(length_{year,type}^{replacement_i}\) are the yearly cumulative road lengths for an infrastructure \(type\) replaced by change in order \(i\)
#' Calculate Yearly Adjusted Road Lengths By Infrastructure Type
#' 
#' Calculates the cumulative yearly adjusted road lengths by infrastructure type accounting for installations and subsequent changes.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param year_cols A vector of the names (char) or indices (int) of the columns containing the years of installations followed by infrastructure changes in order.
#' @param type_cols A vector of the names (char) or indices (int) of the columns containing the infrastructure types of installations followed by infrastructure changes in order.
#' @param type_col The name (char) of the column containing the type.
#' @param len_cols A vector of the names (char) or indices (int) of the columns containing the road lengths of installations followed by infrastructure changes in order.
#' @param out_cols The name (char) of the column containing the calculated yearly road lengths by type.
#' @param out_col The name (char) of the column containing the calculated yearly adjusted road lengths by type.
#' @param repl_suffix A suffix (char) to append to the columns representing the road lengths of replaced infrastructure types from changes.
#' @param ... Additional arguments passed to calc_yearly_len.
#' 
#' @return A data.frame with columns containing the year, type, cumulative road lengths of installations, changes, and replacements, and calculated yearly adjusted road lengths by type.
#' @export
#'
calc_yearly_adj_len <- function(
        df,
        year_cols = c("install_year", "upgrade1_year", "upgrade2_year"),
        type_cols = c("install_type", "upgrade1_type", "upgrade2_type"),
        type_col = "type",
        len_cols = "segment_len",
        out_cols = c("install_len", "upgrade1_len", "upgrade2_len"),
        out_col = "adj_len",
        repl_suffix = "_replaced",
        ...
    ) {
    
    # Convert len_col if char
    len_cols <- rep(len_cols, length(year_cols))
    
    # Check cols same size
    year_cols_n <- length(year_cols)
    type_cols_n <- length(type_cols)
    len_cols_n <- length(len_cols)
    out_cols_n <- length(out_cols)
    if (length(unique(c(year_cols_n, type_cols_n, len_cols_n, out_cols_n))) != 1) {
        stop(glue(
            "The arguments 'year_cols' ({year_cols_n}), 'type_cols' ({type_cols_n}), 'len_cols' ({len_cols_n}), and 'out_cols' ({out_cols_n}) must be the same length."
        ))
    }
    
    # Calc yearly lens by infra type per install or change
    out <- list()
    for (i in 1:length(year_cols)) {
        
        # Get year, type, and len cols
        ycol <- year_cols[[i]]
        tcol <- type_cols[[i]]
        lcol <- len_cols[[i]]
        ocol <- out_cols[[i]]
        
        # Calc yearly len for install or change
        out <- append(
            out,
            calc_yearly_len(
                df,
                year_col = ycol,
                type_col = tcol,
                len_col = lcol,
                out_col = ocol,
                ...
            ) %>%
                rename(
                    "year" := !!ycol,
                    "type" := !!tcol
                ) %>% list
        )
        
        # Calc yearly len for replacement
        if (i > 1) {
            
            # Get repl cols
            tcol_repl <- type_cols[[i - 1]]
            lcol_repl <- len_cols[[i - 1]]
            
            # Filter for repl records only where type is not eq to change type
            df_repl <- df %>% filter(.data[[tcol]] != .data[[tcol_repl]])
            
            # Calc repl len if there are any changes
            has_change <- !is.na(df_repl[[tcol]]) %>% all 
            if (has_change) {
                out <- append(
                    out,
                    calc_yearly_len(
                        df_repl,
                        year_col = ycol,
                        type_col = tcol_repl,
                        len_col = lcol_repl,
                        out_col = glue("{ocol}{repl_suffix}"),
                        ...
                    ) %>%
                    rename(
                        "year" := !!ycol,
                        "type" := !!tcol_repl
                    ) %>% list
                )
            }
        }
    }
    
    # Combine all lens in list to single df
    out <- out %>%
        reduce(
            left_join, by = c("year", "type")
        ) %>%
        ungroup()
    
    # Create template for change and repl cols
    change_cols <- paste0(out_cols[2:out_cols_n])# change cols
    change_cols <- c(change_cols, paste0(out_cols[2:out_cols_n], repl_suffix)) # repl cols
    change_cols_add <- rep(0, length(change_cols)) # set default vals
    names(change_cols_add) <- change_cols
    
    # Add change and repl cols set to 0 if not present
    out <- out %>% add_column(
        !!!change_cols_add[setdiff(names(change_cols_add), names(.))]
    )
    
    # Set NA to 0
    out <- out %>% mutate(
        across(everything(), ~replace_na(., 0))
    )
    
    # Calc yearly adj lens by infra type
    out <- out %>%
        mutate( # added len by infra types due to install or changes
            !!out_col := reduce(across(all_of(out_cols)), `+`)
        ) %>%
        mutate( # removed len by infra types due to replacements
            !!out_col := .data[[out_col]] - reduce(
                across(all_of(
                    paste0(out_cols[2:out_cols_n], repl_suffix)
                )),
                `-`
            )
        )
    
    # Rename type col
    out <- out %>% rename(!!type_col := type)
    return(out)
}

Function 3: Plot Lengths by Year for Generic Types

Plots an area chart showing the cumulative road lengths by a user-defined type for each year.

This is a generic function for user-defined types such as infrastructure or road types.

#' Plot Yearly Road Lengths By Type
#' 
#' Creates an area plot of road lengths by category types.
#'
#' @param df A data.frame with three columns containing the year, type, and road lengths.
#' @param title The title (char) of the plot.
#' @param title_underline Set to TRUE to underline the title.
#' @param x_title The title (char) of the x-axis.
#' @param y_title The title (char) of the y-axis.
#' @param y_suffix The suffix (char) to add to the end of y axis values.
#' @param legend_title The title (char) of the legend.
#' @param legend Set to TRUE to include a legend.
#' @param year_col The name (char) or index (int) of the column containing the years.
#' @param year_min The minimum year (int) to display.
#' @param year_max The maximum year (int) to display.
#' @param year_int The year intervals (int) to display. For example, 1 displays every year, and 2 displays every two years.
#' @param len_col The name (char) or index (int) of the column containing the road lengths.
#' @param type_col The name (char) or index (int) of the column containing the type.
#' @param type_filter A vector (char) of types to remove fomr the plot.
#' @param type_recode A named vector (char) of names representing types and values representing the values to replace each type with.
#' @param line_50km Set to TRUE to draw the 50 km red reference line.
#' @param line_year Set to a year (int) to draw a reference line for a year. If FALSE, a line will not be drawn.
#' @param color_low The bottom color (char) of the type.
#' @param color_high The top color (char) of the type.
#' @return An area ggplot of the cumulative yearly road lengths by type.
#' @export
#'
plot_yearly_len <- function(
        df,
        title = "",
        title_underline = TRUE,
        x_title = "",
        y_title = "",
        y_suffix = " km",
        legend_title = "Type",
        legend = TRUE,
        year_col = "year",
        year_min = FALSE,
        year_max = FALSE,
        year_int = 1,
        len_col = "adj_len",
        type_col = "type",
        type_filter = c(),
        type_recode = c(),
        line_50km = FALSE,
        line_year = FALSE,
        color_low = "#DFEBF7",
        color_high = "#3683BB"
) {
    
    # Filter to start and end years
    if (year_min > 0) {
        df <- df %>% filter(
            .data[[year_col]] >= year_min
        )
    }
    if (year_max > 0) {
        df <- df %>% filter(
            .data[[year_col]] <= year_max
        )
    }
    
    # Filter out particular infrastructure types
    if (length(type_filter) > 0) {
        df <- df %>% filter(
            !.data[[type_col]] %in% type_filter
        )
    }
    
    # Recode and reorder category types
    if (length(type_recode) > 0) {
        
        # Reorder category types
        type_uniq <- unique(df[[type_col]])
        type_reorder <- names(type_recode)
        type_reorder <- c(type_reorder, type_uniq[!type_uniq %in% type_reorder])
        df[[type_col]] <- factor(df[[type_col]], levels = type_reorder)
        
        # Recode category types
        df[[type_col]] <- recode(df[[type_col]], !!!type_recode)
    }
    
    # Create fill colors
    type_n <- length(type_uniq)
    type_colors <- scales::seq_gradient_pal(
        color_low,
        color_high
    )(seq(0, 1, length.out = type_n))
    
    # Create base area plot with legend and labels
    len_max <- max(df[[len_col]], na.rm = TRUE)
    year_max <- max(df[[year_col]], na.rm = TRUE)
    out <- ggplot(
        df,
        aes(
            x = .data[[year_col]],
            y = .data[[len_col]],
            fill = .data[[type_col]],
            order = desc(.data[[type_col]])
        )
    ) +
    geom_area(colour = NA, alpha = 0.7) +
    scale_fill_manual(values = type_colors) +
    geom_line(
        position = "stack",
        size = 0.2
    ) +
    labs(
        x = x_title,
        y = y_title,
        fill = legend_title
    ) +
    guides(
        fill = FALSE,
        color = FALSE
    ) +
    scale_x_continuous(
        breaks = seq(year_min, year_max, by = year_int),
        labels = seq(year_min, year_max, by = year_int),
        limits = c(year_min, year_max)
    ) +
    scale_y_continuous(
        label = scales::label_number(suffix = y_suffix)
    ) +
    theme_minimal() +
    theme(
        plot.margin = unit(c(5,5,5,5), "points")
    )
    
    # Add title
    if (title_underline) {
        out <- out + ggtitle(
            bquote(underline(.(title)))
        )
    } else {
        out <- out + ggtitle(title)
    }
    
    # Add legend
    if (legend) {
        out <- out + guides(fill = guide_legend(
            reverse = FALSE,
            override.aes = list(
                alpha = 0.7,
                color = NA,
                shape = NA
            )
        ))
    }
    
    # Add dotted year ref line
    if (line_year) {
        out <- out + geom_vline(
            xintercept = line_year,
            color = "black",
            linetype = "dashed"
        )
    }
    
    # Add red 50km ref line
    if (line_50km) {
        out <- out + geom_segment( # 50km red line
            aes(
                x = 2009,
                y = 0,
                xend = 2009,
                yend = 50,
                color = "#bb0000",
                hjust = 0.15
            )
        ) +
        geom_segment( # 50km red triangle point down
            aes(
                x = 2009,
                y = 50.01 - (len_max * 0.05),
                xend = 2009,
                yend = 50 - (len_max * 0.05),
                color = "#bb0000",
                hjust = 0.15
            ),
            arrow = arrow(
                length = unit(0.03, "npc"),
                ends = "last",
                type = "closed"
            )
        ) +
        geom_segment( # 50km red triangle point up
            aes(
                x = 2009,
                y = (len_max * 0.05) - 0.01,
                xend = 2009,
                yend = (len_max * 0.05),
                color = "#bb0000",
                hjust = 0.15
            ),
            arrow = arrow(
                length = unit(0.03, "npc"),
                ends = "last",
                type = "closed"
            )
        ) +
        annotate(
            "text",
            x = 2009,
            y = 50,
            label = "50km",
            color = "#bb0000",
            hjust = -0.225
        )
    }
    return(out)
}

Function 3.1: Plot Lengths by Year for Infrastructure Types

Plots area charts of yearly road lengths by infrastructure types for a list of data.

This uses the plot_yearly_len function.

#' Plot Yearly Road Lengths By Infrastructure Type
#' 
#' Creates area plots of road lengths by infrastructure type.
#'
#' @param df_list A list of data.frame containing the install and change years, type, and road segment lengths.
#' @return Multiple area ggplots of the cumulative yearly road lengths by infrastructure type combined with patchwork.
#' @export
#'
plot_yearly_len_infra <- function(df_list) {
    
    # Create infra plots from data
    p <- list()
    for (i in 1:length(df_list)) {
        
        # Get data and plot title
        df <- df_list[[i]]
        ptitle <- names(df_list)[[i]]
        
        # Create and add infra plot to list
        p[[i]] <- calc_yearly_adj_len(df, type_col = settings$type_col_infra) %>%
            plot_yearly_len(
                title = ptitle,
                year_min = settings$year_min,
                year_max = settings$year_max,
                type_col = settings$type_col_infra,
                type_filter = settings$type_filter_infra,
                type_recode = settings$type_recode_infra,
                legend_title = "Infrastructure Type",
                line_50km = TRUE,
                line_year = settings$line_year
            )
    }
    
    # Y-axis title
    y_title <- ggplot() +
        annotate(
            geom = "text",
            x = 1,
            y = 1,
            label = "Total Length (Centreline km)",
            angle = 90,
            size = 5
        ) +
        coord_cartesian(clip = "off")+
        theme_void()
    
    # Combine all infra plots together
    out <- (y_title | wrap_plots(p, nrow = length(p))) +
        plot_annotation(
            title = "Roadways with Dedicated Cycling Infrastructure",
            caption = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
            theme = theme(
                plot.title = element_text(hjust = 0.5, size = 16),
                plot.caption = element_text(hjust = 0.5, size = 14)
            )
        ) +
        plot_layout(widths = c(0.05, 1))
    return(out)
}

Function 3.2: Plot Lengths by Year for Road Types

Plots area charts of yearly road lengths by overall road type and by infrastructure separated by each road type.

This uses the plot_yearly_len function.

#' Plot Yearly Road Lengths By Road Type
#'
#' Creates area plots of road lengths by overall road type, and by infrastructure per road type.
#'
#' @param df The data.frame containing the install and change years, type, and road segment types and lengths. 
#' @return Multiple area ggplots of the cumulative yearly road lengths by road type combined with patchwork.
#' @export
#'
plot_yearly_len_road <- function(df, title = "Roadways with Dedicated Cycling Infrastructure") {
    
    # Create list to store plots
    p <- list()

    # Plot overall road types
    p[[1]] <- calc_yearly_len(
        df,
        year_col = settings$year_col_road,
        type_col = settings$type_col_road
    ) %>%
        plot_yearly_len(
            title = title,
            title_underline = FALSE,
            year_col = settings$year_col_road,
            year_min = settings$year_min,
            year_max = settings$year_max,
            x_title = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
            y_title = "Total Length (Centreline km)",
            legend_title = "Roadway Type",
            type_col = settings$type_col_road,
            type_recode = settings$type_recode_road,
            len_col = "len",
            line_50km = FALSE,
            line_year = settings$line_year,
            color_low = "#C1DDB3",
            color_high = "#297A22"
        ) +
        theme(
            plot.title = element_text(size = 18),
            plot.margin = margin(0, 0, 0, 0, "pt")
        )
    
    # Plot arterial, collector, and local road by infra
    rtypes <- c("Arterial", "Collector", "Local")
    for (i in 1:length(rtypes)) {
        
        # Get road type
        r <- rtypes[i]
        
        # Create infra plot for road type
        p[[i + 1]] <- calc_yearly_adj_len(
            df %>% filter(road_type == r),
            type_col = settings$type_col_infra
        ) %>%
            plot_yearly_len(
                title = sprintf("%s Roadways", r),
                title_underline = FALSE,
                line_50km = FALSE,
                line_year = settings$line_year,
                year_int = 2,
                x_title = sprintf("Years (%s-%s)", settings$year_min, settings$year_max),
                y_title = "Total Length (Centreline km)",
                year_min = settings$year_min,
                year_max = settings$year_max,
                type_col = settings$type_col_infra,
                type_filter = settings$type_filter_infra,
                type_recode = settings$type_recode_infra,
                legend_title = "Infrastructure Type"
            ) +
            theme(
                plot.title = element_text(size = 14),
                plot.margin = margin(0, 12, 0, 0, "pt")
            )
    }
    
    # Plot horizontal gradient bar
    grad_bar <-  ggplot(data.frame(x = 1:4), aes(x = x, y = 1, color = x)) +
        geom_line(size = 4) +
        scale_color_gradient(low = "#C1DDB3", high = "#297A22") +
        theme_void() +
        guides(color = FALSE) +
        theme(
            axis.title = element_blank(),
            axis.text = element_blank(),
            axis.ticks = element_blank(),
            axis.line = element_blank(),
            plot.margin = margin(0, 0, 0, 0, "pt")
        )
    
    # Plot overall and road type plots together
    out <- ( # overall plot
        plot_spacer() +
        p[[1]] +
        plot_spacer() +
        plot_layout(
            widths = c(0.25, 0.35, 0.2)
        )
    ) / ( # gradient bar
        plot_spacer() +
        grad_bar +
        plot_spacer() +
        plot_layout(widths = c(-0.8, 10, -1.1))
    ) / ( # infra plots
        p[[2]] +
        p[[3]] +
        p[[4]]
    ) + plot_layout(
        heights = c(12, 1, 8)
    ) + plot_annotation( # A B tags
        tag_levels = list(c("A", "", "B", "", ""))
    ) & theme(
        plot.tag = element_text(face = "bold", size = 12)
    )
    return(out)
}

Function 4: Plot Yearly Differences

Plots a bar chart of differences between two columns containing years.

This function is used to check the differences in installation years between the city’s data and the verified data.

#' Plot Yearly Differences
#'
#' Creates a bar plot of the differences between two years.
#'
#' @param df The data.frame containing the two columns with the years.
#' @param year_col1 The name (char) or index (int) of the first year column.
#' @param year_col2 The name (char) or index (int) of the second year column to be subtracted from.
#' @param year_col1_name The name alias (char) of the first year column year_col1.
#' @param year_col2_name The name alias (char) of the second year column year_col2.
#' @param year_min The minimum year (int) to calculate differences for.
#' @param year_max The maximum year (int) to calculate differences for.
#' @param title The title (char) of the plot.
#' @param title_n Set to TRUE to add the number of total segments considered.
#' @param x_title The title (char) of the x-axis.
#' @param y_title The title (char) of the y-axis.
#' @param x_breaks The number (int) of breaks to show on the x-axis. Set to FALSE to let ggplot automatically decide.
#' @return A ggplot of yearly differences (year_col2 - year_col1), displaying the proportion of rows for each difference in years.
#' @export
#'
plot_yearly_diff <- function(
        df,
        year_col1 = "install_year_orig",
        year_col2 = "install_year",
        year_col1_name = "City Year",
        year_col2_name = "Verified Year",
        year_min = settings$year_min,
        year_max = settings$year_max,
        title = sprintf(
            "Difference in Years, Comparing %s and %s",
            year_col1_name,
            year_col2_name
        ),
        title_n = TRUE,
        x_title = sprintf(
            "Difference in Years (%s - %s)",
            year_col2_name,
            year_col1_name
        ),
        y_title = "Proportion of Total Segments",
        x_breaks = 15
) {
    ydiff <- df
    
    # Filter within min year
    if (year_min) {
        ydiff <- ydiff %>% filter(
            .data[[year_col1]] >= year_min | .data[[year_col2]] >= year_min
        )
    }
    
    # Filter within max year
    if (year_max) {
        ydiff <- ydiff %>% filter(
            .data[[year_col1]] <= year_max | .data[[year_col2]] <= year_max
        )
    }
    
    # Add n to title
    if (title_n) {
        title <- sprintf("%s (n=%s)", title, nrow(ydiff))
    }
    
    # Calc yearly diff
    ydiff <- ydiff %>%
        mutate(year_diff = install_year - install_year_orig) %>%
        count(year_diff)
    
    # Plot yealy diffs
    out <- ydiff %>% 
        ggplot(aes(
            x = year_diff,
            y = (n / sum(n)) * 100
        )) +
        geom_bar(
            stat = "identity",
            color = "#332a94",
            fill = "#c3d5e4",
            width = 1
        ) +
        labs(
            title = title,
            x = x_title,
            y = y_title
        ) +
        scale_y_continuous(
            label = scales::label_number(suffix = "%")
        ) +
        theme(
            plot.title = element_text(size = 12)
        )
    
    # Set x interval breaks
    if (x_breaks) {
        out <- out + scale_x_continuous(
            breaks = scales::breaks_pretty(x_breaks)
        )
    }
    return(out)
}

Data

Load raw data provided by Konrad Samsel.

Vancouver Raw Data

# Load raw data
vanc_bikeways <- read_csv("../data/vancouver_bikeways_2009_2022_v1.csv")
Rows: 745 Columns: 89
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (73): bike_rout0, street_na0, bikeway_t0, subtype, status, street_se0, o...
dbl (16): OID_, object_id, speed_lim0, year_of_c0, upgrade_y0, ID_DATAENTRY,...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
vanc_roads <- read_csv("../data/vancouver_roads_2009_2022_v1.csv")
Rows: 780 Columns: 72
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (61): ID_ROUTE, DPR_CHECK_FLAG, DPR_ENTRY, DPR_EXCL_FLAG, DPR_EXCL1318_R...
dbl (11): ID_CITY, ID_DATAENTRY, DPR_ORDER, ATR_SEGMENT_LENGTH, ATR_SPEEDLIM...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Combine raw data
vanc <- vanc_bikeways %>%
    select(
        ID_DATAENTRY,
        INST_YR_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_TYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        vanc_roads %>% select(
            ID_DATAENTRY,
            ATR_SEGMENT_TYPE
        ),
        by = "ID_DATAENTRY"
    ) %>%
    rename(
        id = ID_DATAENTRY,
        install_year_orig = INST_YR_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_TYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = ATR_SEGMENT_TYPE
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Arterial"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Collector",
                "Secondary Arterial",
                "Sec Arterial"
            ) ~ "Collector",
            segment_type %in% c( # local equiv
                "Lane",
                "Residential",
                "Leased",
                "Recreational"
            ) ~ "Local",
            .default = segment_type
        )
    )
vanc

Calgary Raw Data

# Load raw data
calg_bikeways <- read_csv("../data/calgary_bikeways_2009_2022_v1.csv")
Rows: 750 Columns: 54
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (41): STATUS, TYPE, BICYCLE_CLASS, COMFORT_LEVEL, CURRENT_TYPE_VERIFIED...
dbl  (11): ORIG_ID, ATR_SEGMENT_LENGTH, INST_YR, UPGR2_YR, SHAPE_ID, STARTIN...
date  (2): CREATED_DT, MODIFIED_DT

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
calg_roads <- read_csv("../data/calgary_roads_2009_2022_v1.csv")
Rows: 4170 Columns: 39
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (18): status, type, bicycle_cl, comfort_le, date_creat, date_modif, ful...
dbl  (15): OID_, len_m, lenm, startx, starty, endx, endy, shape_id, OBJECTID...
lgl   (1): length
time  (5): time_creat, time_modif, time_creat_1, time_modif_1, time_mod_2

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Combine raw data
calg <- calg_bikeways %>%
    select(
        SHAPE_ID,
        YEAR_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_HTYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        calg_roads %>% select(
            shape_id,
            ctp_class
        ),
        by = join_by(SHAPE_ID == shape_id)
    ) %>%
    rename(
        id = SHAPE_ID,
        install_year_orig = YEAR_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_HTYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = ctp_class
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Arterial Street",
                "Industrial Arterial",
                "Local Arterial",
                "Parkway",
                "Urban Boulevard"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Neighbourhood Boulevard",
                "Collector",
                "Primary Collector",
                "Skeletal Road"
            ) ~ "Collector",
            segment_type %in% c( # local equiv
                "Access Route",
                "Residential Street",
                "Activity Center Street",
                "Historic Road Allowance",
                "Lanes (Alleys)",
                "Industrial Street"
            ) ~ "Local",
            .default = segment_type
        )
    )
calg

Toronto Raw Data

toron_raw <- st_read("../data/raw/Toronto AS Export/Toronto_AS_1323.shp")
Reading layer `Toronto_AS_1323' from data source 
  `/Users/rrwen/Desktop/recovr-infracycle/data/raw/Toronto AS Export/Toronto_AS_1323.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1323 features and 66 fields
Geometry type: MULTILINESTRING
Dimension:     XY
Bounding box:  xmin: -79.63039 ymin: 43.58221 xmax: -79.11803 ymax: 43.85546
Geodetic CRS:  WGS 84
toron_raw

Preprocessing

Toronto Preprocessed Data

Note 1: Working on getting complete data.

Note 2: Using this one for now.

# Load raw data
toron_bikeways <- read_csv("../data/toronto_bikeways_2009_2022_v1.csv")
Rows: 326 Columns: 53
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (35): CITY_INFRA_HIGHORDER, CITY_INFRA_LOWORDER, STREET_NAME, FROM_STRE...
dbl  (16): ID_OID, ID_DATAENTRY, ID_1_OBJ2, OBJECTID, CITY_INST_YR, CITY_UPG...
lgl   (1): DPR_EXCL_FLAG
dttm  (1): CITY_LAST_REVIEWED

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
toron_roads <- read_csv("../data/toronto_roads_2009_2022_v1.csv")
Rows: 331 Columns: 59
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (14): STREET_7, FROM_ST8, TO_STRE9, INFRA_L15, INFRA_H20, LINEAR_26, LI...
dbl  (28): OID_, _id1, OBJECTI2, SEGMENT3, INSTALL4, UPGRADE5, CONVERT28, st...
lgl  (16): PRE_AMA6, ROADCLA10, CNPCLAS11, SURFACE12, OWNER13, DIR_LOW14, SE...
dttm  (1): LAST_ED26

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Combine raw data
toron <- toron_bikeways %>%
    select(
        ID_OID,
        INSTALLED_ORIG,
        INST_YR,
        INST_MIN_HTYPE,
        UPGR1_YR,
        UPGR1_MIN_HTYPE,
        UPGR2_YR,
        UPGR2_MIN_HTYPE,
        ATR_SEGMENT_LENGTH
    ) %>%
    left_join(
        toron_roads %>% select(
            OID_,
            FEATURE36
        ),
        by = join_by(ID_OID == OID_)
    ) %>%
    rename(
        id = ID_OID,
        install_year_orig = INSTALLED_ORIG,
        install_year = INST_YR,
        install_type = INST_MIN_HTYPE,
        upgrade1_year = UPGR1_YR,
        upgrade1_type = UPGR1_MIN_HTYPE,
        upgrade2_year = UPGR2_YR,
        upgrade2_type = UPGR2_MIN_HTYPE,
        segment_len = ATR_SEGMENT_LENGTH,
        segment_type = FEATURE36
    ) %>%
    mutate(
        segment_len = segment_len / 1000,
        road_type = case_when( # create road types
            segment_type %in% c( # arterial equiv
                "Major Arterial",
                "Major Arterial Ramp",
                "Minor Arterial"
            ) ~ "Arterial",
            segment_type %in% c( # collector equiv
                "Collector"
            ) ~ "Collector",
            segment_type %in% c(  # local equiv
                "Local",
                "Other"
            ) ~ "Local",
            .default = segment_type
        )
    )
toron

Figures

Figure 1: Flow diagram of inclusion criteria for bikeway segments in Vancouver, Calgary, and Toronto.

This flowchart provides a high-level overview of the segment inclusions and exclusions for each municipality. Data from Calgary were specific to on-street routes only. For detailed flow diagrams specific to each municipality, please refer to the Appendix.

grViz("
digraph {
    rankdir = LR
    node[
        shape = box,
        width = 2.75,
        height = 1.35,
        style = filled,
        fillcolor = white,
        penwidth = 1.5,
        fontname = 'Arial'
    ]
    layout = neato
    
    filter1[
        label = 'Filtering',
        height = 0.25,
        shape = plaintext,
        style='', pos = '1.6,1.425!'
    ]
    filter2[
        style = invis,
        pos = '1.6,-3.9!'
    ]
    filter1 -> filter2 [style = dashed, dir = none, color = '#b0b0b0']
    
    screen1[
        label = 'Screening',
        height = 0.25,
        shape = plaintext,
        style='', pos = '4.85,1.425!'
    ]
    screen2[
        style = invis,
        pos = '4.85,-3.9!'
    ]
    screen1 -> screen2 [style = dashed, dir = none, color = '#b0b0b0']
    
    open_data[
        label = 'Open Data',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '0,1!'
    ]
    elig_data[
        label = 'Eligible Segments',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '3.25,1!'
    ]
    incl_data[
        label = 'Inclusions',
        height = 0.5,
        fillcolor = '#d7e9fe',
        pos = '6.5,1!'
    ]
    
    open_vanc[
        label = <<b>Vancouver</b><br/>N = @@1-1 Segments<br/><i>(@@2-1)<br/>Downloaded: January 2023</i>>,
        pos = '0,-0.06!'
    ]
    open_calg[
        label = <<b>Calgary</b><br/>N = @@1-2 Segments<br/><i>(@@2-2)<br/>Downloaded: January 2023</i>>,
        pos = '0,-1.65!'
    ]
    open_toron[
        label = <<b>Toronto</b><br/>N = @@1-3 Segments<br/><i>(@@2-3)<br/>Downloaded: January 2023</i>>, 
        pos = '0,-3.24!'
    ]
    
    elig_vanc[
        label = <n = @@3-1 Segments<br/>(@@4-1)<br/><i><b>Exclusions</b>@@5-1@@6-1@@7-1</i>>,
        pos = '3.25,-0.06!'
    ]
    
    elig_calg[
        label = <n = @@3-2 Segments<br/><i>(@@4-2)<br/><b>Exclusions</b>@@5-2@@6-2@@7-2</i>>,
        pos = '3.25,-1.65!'
    ]
    elig_toron[
        label = <n = @@3-3 Segments<br/><i>(@@4-3)<br/><b>Exclusions</b>@@5-3@@6-3@@7-3</i>>,
        pos = '3.25,-3.24!'
    ]
    
    incl_vanc[
        label = <n = @@8-1 Segments<br/><i>(@@9-1)<br/><b>Exclusions</b>@@10-1@@11-1</i>>,
        pos = '6.5,-0.06!'
    ]
    incl_calg[
        label = <n = @@8-2 Segments<br/><i>(@@9-2)<br/><b>Exclusions</b>@@10-2@@11-2</i>>,
        pos = '6.5,-1.65!'
    ]
    incl_toron[
        label = <n = @@8-3 Segments<br/><i>(@@9-3)<br/><b>Exclusions</b>@@10-3@@11-3</i>>,
        pos = '6.5,-3.24!'
    ]
    
    note[
        label=<<i>@@12</i>>,
        style = '',
        shape = plaintext,
        fontsize = 12,
        pos = '3.25,-4.15!'
    ]
    
    open_vanc -> elig_vanc -> incl_vanc
    open_calg -> elig_calg -> incl_calg
    open_toron -> elig_toron -> incl_toron
}

[1]: c('3664', '4166', '1323') # open segments
[2]: c('342.1 km', '569.7 km', '755.8 km') # open km
[3]: c('780', '782', '331') # eligible segments
[4]: c('71.2 km', '88.7 km', '205.4 km') # eligible km
[5]: c('<br/>Ineligible: n=2883', '<br/>Ineligible: n=3383', '<br/>Ineligible: n=992') # eligible exclusions
[6]: c('<br/>Duplicates n=1', '', '') # eligble duplicates
[7]: c('', '<br/>No Polyline Data: n=1', '') # eligible polyline data
[8]: c('745', '750', '326') # inclusion segments
[9]: c('69.9 km', '85 km', '203.5 km') # inclusion km
[10]: c('<br/>*Misclassifications: n=34', '<br/>*Misclassifications: n=32', '<br/>*Misclassifications: n=5') # inclusion misclassifications
[11]: c('<br/>Duplicates: n=1', '', '') # inclusion duplicates
[12]: '*Denotes segments misclassified as an ineligible type (off-street path, shared road, or inactive temporary infrastructure)'
")
%>%
    export_svg %>%
    charToRaw %>%
    rsvg_png("test.png")

Figure 2: Changes in dedicated cycling infrastructure between 2009 and 2022 for Vancouver, Calgary, and Toronto by infrastructure category.

Assessed using roadway centreline-km, with infrastructure classifications determined by the most protective element present along each road segment.

plot_yearly_len_infra(list(
    "Vancouver, CA" = vanc,
    "Calgary, CA" = calg,
    "Toronto, CA" = toron
))

Figure 4: Changes in Dedicated On-Street Infrastructure Since January 2020 for Vancouver, Calgary, and Toronto.

New installations of dedicated infrastructure are denoted in green, upgrades from a previous dedicated infrastructure type are denoted in orange. Mapped in ArcGIS Pro 3.0.1.

Appendix 1 - Supplementary Results

Supplementary Figure 4: Changes in dedicated cycling infrastructure between 2009 and 2021 for the Municipality of Vancouver, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    vanc,
    title = "Roadways with Dedicated Cycling Infrastructure (Vancouver, CA)"
)

Supplementary Figure 5: Changes in dedicated cycling infrastructure between 2009 and 2022 for the Municipality of Calgary, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    calg,
    title = "Roadways with Dedicated Cycling Infrastructure (Calgary, CA)"
)

Supplementary Figure 6: Changes in dedicated cycling infrastructure between 2009 and 2022 for the Municipality of Toronto, CA.

By (A) roadway classification, and (B) infrastructure distribution within each road class. Assessed using roadway centreline-km, with infrastructure classification determined by the most protective element present along each road segment.

plot_yearly_len_road(
    toron,
    title = "Roadways with Dedicated Cycling Infrastructure (Toronto, CA)"
)

Supplementary Figure 7: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Vancouver, CA.

Any data where a city provided installation year was missing or the verified year occurred earlier than the start of the study period (2009) has been excluded from analysis, yielding n = 252 segments. The graph shows that 83.3% of the included segments had the correct installation year as per the city’s data, and 97.6% were accurate within a range of ±1 year.

plot_yearly_diff(
    vanc,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Vancouver, CA"
)

Supplementary Figure 8: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Calgary, CA.

Any data where a city provided installation year was missing or the verified year occurred earlier than the start of the study period (2009) has been excluded from analysis, yielding n=670 segments. The graph shows that 42.1% of the included segments had the correct installation year as per the city’s data, and 62.8% were accurate within a range of ±1 year.

plot_yearly_diff(
    calg,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Calgary, CA"
)

Supplementary Figure 9: A comparative analysis between municipal data and verified data on the installation years for cycling infrastructure in Toronto, CA.

Any data where a city provided installation year was missing or the verified year occurred than the start of the study period (2009) has been excluded from analysis, yielding n=192 segments. The graph shows that 75.5% of the included segments had the correct installation year as per the city’s data, and 78.1% were accurate within a range of ±1 year.

plot_yearly_diff(
    toron,
    title = "Difference in Installation Years, Comparing City Data and Verified Data: Toronto, CA"
)

Contributions

Richard Wen developed reproducible R code and organized the data based on Konrad Samsel’s draft manuscript and previous R code. Konrad Samsel prepared draft manuscript, raw data, and provided consultation on data and methods.

Acknowledgements

Linda Rothman and Brice Batomen provided supervision, project administration, resources, funding, and review/editing for the draft manuscript.

LS0tCnRpdGxlOiAiUGVkYWxsaW5nIEZvcndhcmQ6IFRoZSBFdm9sdXRpb24gb2YgRGVkaWNhdGVkIEN5Y2xpbmcgSW5mcmFzdHJ1Y3R1cmUgaW4gQ2FuYWRpYW4gQ2l0aWVzIGZyb20gMjAxMCB0byAyMDIyIgpzdWJ0aXRsZTogIlIgQ29kZSBmb3IgRmlndXJlcyBhbmQgVGFibGVzIgphdXRob3I6Ci0gIlJpY2hhcmQgV2VuIHJpY2hhcmQud2VuQHV0b3JvbnRvLmNhIgotICJLb25yYWQgU2Ftc2VsIGtvbnJhZC5zYW1zZWxAbWFpbC51dG9yb250by5jYSIKLSAiQnJpY2UgQmF0b21lbiBicmljZS5rdWltaUB1dG9yb250by5jYSIKLSAiTGluZGEgUm90aG1hbiBsaW5kYS5yb3RobWFuQHRvcm9udG9tdS5jYSIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgprbml0OiB8CiAgICAoZnVuY3Rpb24oaW5wdXRfcm1kLCAuLi4pIHsKICAgIHJtYXJrZG93bjo6cmVuZGVyKAogICAgICAgIGlucHV0X3JtZCwKICAgICAgICBybWFya2Rvd246Omh0bWxfbm90ZWJvb2soCiAgICAgICAgICAgIHRvYyA9IFRSVUUsCiAgICAgICAgICAgIHRvY19mbG9hdCA9IFRSVUUsCiAgICAgICAgICAgIGhpZ2hsaWdodCA9ICJ6ZW5idXJuIgogICAgICAgICksCiAgICAgICAgb3V0cHV0X2RpciA9ICIuLi9kb2NzIiwKICAgICAgICBvdXRwdXRfZmlsZSA9ICJpbmRleCIsIC4uLikKICAgIHJlbmFtZWQgPC0gZmlsZS5yZW5hbWUoIi4uL2RvY3MvaW5kZXgubmIuaHRtbCIsICIuLi9kb2NzL2luZGV4Lmh0bWwiKQogICAgfSkKLS0tCgpgYGB7ciBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFKQpgYGAKCiMgSW5zdGFsbGF0aW9uCgoxLiBJbnN0YWxsIFtSXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLykKMi4gSW5zdGFsbCBbUlRvb2xzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8pIGlmIHlvdSBhcmUgb24gV2luZG93cwozLiBJbnN0YWxsIFtSU3R1ZGlvXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcC8pCgpSIGFuZCBSTWFya2Rvd24gaW4gUlN0dWRpbyAodmVyc2lvbiAyMDIzLjA2LjErNTI0KSB3YXMgdXNlZCB0byBnZW5lcmF0ZSB0aGlzIGRvY3VtZW50OgoKYGBge3IgZWNobz1GQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgTGlicmFyaWVzCgpJbnN0YWxsIFIgbGlicmFyaWVzIGlmIG5lZWRlZC4KCmBgYHtyIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInJtYXJrZG93biIpCmluc3RhbGwucGFja2FnZXMoImJvb2tkb3duIikKaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJnbHVlIikKaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKaW5zdGFsbC5wYWNrYWdlcygiZ2d0ZXh0IikKaW5zdGFsbC5wYWNrYWdlcygic2NhbGVzIikKaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIikKaW5zdGFsbC5wYWNrYWdlcygiRGlhZ3JhbW1lUiIpCmluc3RhbGwucGFja2FnZXMoIkRpYWdyYW1tZVJzdmciKQppbnN0YWxsLnBhY2thZ2VzKCJ3ZWJzaG90MiIpCmluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpCmluc3RhbGwucGFja2FnZXMoInJzdmciKQppbnN0YWxsLnBhY2thZ2VzKCJzZiIpCmBgYAoKTG9hZCBSIGxpYnJhcmllcy4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoRGlhZ3JhbW1lUikKbGlicmFyeShEaWFncmFtbWVSc3ZnKQpsaWJyYXJ5KGdndGV4dCkKbGlicmFyeShnbHVlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkocnN2ZykKbGlicmFyeShzZikKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBTZXR0aW5ncwoKYGBge3J9CnNldHRpbmdzIDwtIGxpc3QoKQoKIyBJbmZyYXN0cnVjdHVyZSB0eXBlcyBpbiBvcmRlcgpzZXR0aW5ncyR0eXBlX3JlY29kZV9pbmZyYSA8LSBjKAoJUEJMID0gIkN5Y2xlIFRyYWNrIiwKCUJVRiA9ICJCdWZmZXJlZCBMYW5lIiwKCVBMID0gIlBhaW50ZWQgTGFuZSIsCglMU0IgPSAiTG9jYWwgU3RyZWV0XG5CaWtld2F5IgopCgojIEluZnJhc3RydWN0dXJlIHR5cGVzIHRvIHJlbW92ZQpzZXR0aW5ncyR0eXBlX2ZpbHRlcl9pbmZyYSA8LSBjKCJOIiwgIk5vbmUiLCAiU1IiKQoKIyBSb2FkIHR5cGVzIGluIG9yZGVyCnNldHRpbmdzJHR5cGVfcmVjb2RlX3JvYWQgPC0gYygKCUFydGVyaWFsID0gIkFydGVyaWFsIiwKCUNvbGxlY3RvciA9ICJDb2xsZWN0b3IiLAoJTG9jYWwgPSAiTG9jYWwiCikKCiMgQ29sdW1uIHJlZmVyZW5jZXMKc2V0dGluZ3MkeWVhcl9jb2xfcm9hZCA8LSAiaW5zdGFsbF95ZWFyIgpzZXR0aW5ncyR0eXBlX2NvbF9yb2FkIDwtICJyb2FkX3R5cGUiCnNldHRpbmdzJHR5cGVfY29sX2luZnJhIDwtICJpbmZyYV90eXBlIgoKIyBTZXQgeWVhcnMgb2YgaW50ZXJlc3QKc2V0dGluZ3MkeWVhcl9taW4gPC0gMjAwOQpzZXR0aW5ncyR5ZWFyX21heCA8LSAyMDIyCgojIFBsb3Qgc2V0dGluZ3MKc2V0dGluZ3MkbGluZV95ZWFyIDwtIDIwMTkKYGBgCgojIEZ1bmN0aW9ucwoKIyMgRnVuY3Rpb24gMTogQ2FsY3VsYXRlIFllYXJseSBSb2FkIExlbmd0aAoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiBjYWxjdWxhdGVzIHllYXJseSByb2FkIGxlbmd0aHMgYnkgaW5mcmFzdHJ1Y3R1cmUgdHlwZSB1c2luZyBjdW11bGF0aXZlIHN1bXMgYW5kIGZpbGxpbmcgaW4gbWlzc2luZyB5ZWFycyBhbmQgdHlwZXMuCgpGb3IgYSBnaXZlbiBpbmZyYXN0cnVjdHVyZSB0eXBlLCB0aGUgdG90YWwgcm9hZCBsZW5ndGggZm9yIGEgZ2l2ZW4geWVhciBpcyBleHByZXNzZWQgYmVsb3c6CgokJApsZW5ndGhfe3llYXIsdHlwZX0gPSBmKHllYXIsdHlwZSkgPSBcc3VtX3tpPXllYXJfe21pbn19Xnt5ZWFyfWxfe2ksIHR5cGV9CiQkCgpXaGVyZToKCi0gICAkeWVhciQgaXMgdGhlIGdpdmVuIHllYXIKLSAgICR0eXBlJCBpcyB0aGUgaW5mcmFzdHJ1Y3R1cmUgdHlwZQotICAgJHllYXJfe21pbn0kIGlzIHRoZSBlYXJsaWVzdCB5ZWFyIGF2YWlsYWJsZSBpbiB0aGUgZGF0YQotICAgJGxfe2ksdHlwZX0kIGlzIHRoZSByb2FkIGxlbmd0aCAkbCQgZm9yIHByZXZpb3VzIHllYXJzICRpJCBhbmQgaW5mcmFzdHJ1Y3R1cmUgJGokCi0gICAkbF97aSx0eXBlfSQgaXMgc2V0IHRvIDAgaWYgdGhlcmUgaXMgbm8gZGF0YQoKYGBge3J9CgojJyBDYWxjdWxhdGUgWWVhcmx5IFJvYWQgTGVuZ3RocyBCeSBJbmZyYXN0cnVjdHVyZSBUeXBlCiMnIAojJyBDYWxjdWxhdGVzIHRoZSBjdW11bGF0aXZlIHllYXJseSByb2FkIGxlbmd0aHMgYnkgaW5mcmFzdHJ1Y3R1cmUgdHlwZSB3aXRob3V0IGNvbnNpZGVyaW5nIGluZnJhc3RydWN0dXJlIGNoYW5nZXMuCiMnCiMnIEBwYXJhbSBkZiBBIGRhdGEuZnJhbWUgd2l0aCB0aHJlZSBjb2x1bW5zIGNvbnRhaW5pbmcgdGhlIHllYXIsIHR5cGUsIGFuZCByb2FkIGxlbmd0aHMuCiMnIEBwYXJhbSB5ZWFyX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSB5ZWFycy4KIycgQHBhcmFtIHR5cGVfY29sIFRoZSBuYW1lIChjaGFyKSBvciBpbmRleCAoaW50KSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGluZnJhc3RydWN0dXJlIHR5cGUKIycgQHBhcmFtIGxlbl9jb2wgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGUgcm9hZCBsZW5ndGhzLgojJyBAcGFyYW0gb3V0X2NvbCBUaGUgbmFtZSAoY2hhcikgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIHllYXJseSByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycKIycgQHJldHVybiBBIGRhdGEuZnJhbWUgd2l0aCB0aHJlZSBjb2x1bW5zIGNvbnRhaW5pbmcgdGhlIHllYXIsIHR5cGUsIGFuZCBjYWxjdWxhdGVkIHllYXJseSByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycgQGV4cG9ydAojJwpjYWxjX3llYXJseV9sZW4gPC0gZnVuY3Rpb24oCgkJZGYsCgkJeWVhcl9jb2wgPSAiaW5zdGFsbF95ZWFyIiwKCQl0eXBlX2NvbCA9ICJpbnN0YWxsX3R5cGUiLAoJCWxlbl9jb2wgPSAic2VnbWVudF9sZW4iLAoJCW91dF9jb2wgPSAibGVuIiwKCQl5ZWFyX21pbiA9IHNldHRpbmdzJHllYXJfbWluLAoJCXllYXJfbWF4ID0gc2V0dGluZ3MkeWVhcl9tYXgKCSkgewoJCgkjIENvbnZlcnQgZGF0YSB0eXBlcwoJZGZbW3llYXJfY29sXV0gPC0gYXMuaW50ZWdlcihkZltbeWVhcl9jb2xdXSkKCWRmW1t0eXBlX2NvbF1dIDwtIGFzLmNoYXJhY3RlcihkZltbdHlwZV9jb2xdXSkKCWRmW1tsZW5fY29sXV0gPC0gYXMubnVtZXJpYyhkZltbbGVuX2NvbF1dKQoJCgkjIFJlbW92ZSByb3dzIHdpdGggZW1wdHkgdHlwZQoJb3V0IDwtIGRmICU+JSBmaWx0ZXIoCgkJIWlzLm5hKC5kYXRhW1t0eXBlX2NvbF1dKQoJKQoJCgkjIEZpbHRlciB0byBtaW4gYW5kIG1heCB5ZWFycwoJaWYgKHllYXJfbWluID4gMCkgewoJCWRmIDwtIGRmICU+JSBmaWx0ZXIoCgkJCS5kYXRhW1t5ZWFyX2NvbF1dID49IHllYXJfbWluCgkJKQoJfSBlbHNlIHsKCQl5ZWFyX21pbiA8LSBtaW4ob3V0W1t5ZWFyX2NvbF1dLCBuYS5ybSA9IFRSVUUpCgl9CglpZiAoeWVhcl9tYXggPiAwKSB7CgkJZGYgPC0gZGYgJT4lIGZpbHRlcigKCQkJLmRhdGFbW3llYXJfY29sXV0gPD0geWVhcl9tYXgKCQkpCgl9IGVsc2UgewoJCXllYXJfbWF4IDwtIG1heChvdXRbW3llYXJfY29sXV0sIG5hLnJtID0gVFJVRSkKCX0KCQoJIyBBZGQgZHVtbXkgbGVuIGZvciBlYWNoIHR5cGUgYW5kIHllYXIgY29tYm8KCSMgQ292ZXJzIGNhc2VzIHdoZXJlIHR5cGUgYW5kIHllYXIgY29tYm8gZG9lcyBub3QgZXhpc3QKCSMgRS5nLiBObyBuZXcgUEwgaW5zdGFsbHMgaW4gMjAyMSwgaGVuY2UgYSByZWNvcmQgUEwgaW4gMjAyMSBkb2VzIG5vdCBleGlzdAoJdHlwZV91bmlxIDwtIHVuaXF1ZShvdXRbW3R5cGVfY29sXV0pCgl0eXBlX24gPC0gbGVuZ3RoKHR5cGVfdW5pcSkKCXllYXJfdW5pcSA8LSB5ZWFyX21pbjp5ZWFyX21heAoJeWVhcl9uIDwtIGxlbmd0aCh5ZWFyX3VuaXEpCglvdXQgPC0gb3V0ICU+JSBhZGRfcm93KAoJCSEheWVhcl9jb2wgOj0gcmVwKHllYXJfdW5pcSwgZWFjaCA9IHR5cGVfbiksCgkJISF0eXBlX2NvbCA6PSByZXAodHlwZV91bmlxLCB5ZWFyX24pLAoJCSEhbGVuX2NvbCA6PSByZXAoMCwgdHlwZV9uICogeWVhcl9uKQoJKQoJCgkjIENhbGMgY3Vtc3VtIGZvciBlYWNoIG5vbi1lbXB0eSB0eXBlIG9yZGVyZWQgYnkgeWVhcgoJb3V0IDwtIG91dCAlPiUKCQlhcnJhbmdlKC5kYXRhW1t5ZWFyX2NvbF1dKSAlPiUKCQlncm91cF9ieSguZGF0YVtbdHlwZV9jb2xdXSkgJT4lCgkJbXV0YXRlKAoJCQkhIW91dF9jb2wgOj0gY3Vtc3VtKC5kYXRhW1tsZW5fY29sXV0pCgkJKQoKCSMgR2V0IHRoZSBsYXN0IGN1bXN1bSBmb3IgZWFjaCB5ZWFyIGFuZCB0eXBlCglvdXQgPC0gb3V0ICU+JQoJCWdyb3VwX2J5KC5kYXRhW1t5ZWFyX2NvbF1dLCAuZGF0YVtbdHlwZV9jb2xdXSkgJT4lCgkJYXJyYW5nZShkZXNjKHJvd19udW1iZXIoKSkpICU+JQoJCXNsaWNlKDEpCgkKCSMgUmV0dXJuIG9ubHkgdGhlIGNvbHVtbnMgc3BlYwoJb3V0IDwtIG91dCAlPiUgc2VsZWN0KGMoCgkJCXllYXJfY29sLAoJCQl0eXBlX2NvbCwKCQkJb3V0X2NvbAoJCSkpCglyZXR1cm4ob3V0KQp9CmBgYAoKIyMgRnVuY3Rpb24gMjogQ2FsY3VsYXRlIFllYXJseSBBZGp1c3RlZCBSb2FkIExlbmd0aAoKVGhlIGZvbGxvd2luZyBmdW5jdGlvbiBjYWxjdWxhdGVzIHllYXJseSBhZGp1c3RlZCByb2FkIGxlbmd0aHMgYnkgaW5mcmFzdHJ1Y3R1cmUgdHlwZSB1c2luZyBjdW11bGF0aXZlIHN1bXMgYW5kIGZpbGxpbmcgaW4gbWlzc2luZyB5ZWFycyBhbmQgdHlwZXMuCgpGb3IgYSBnaXZlbiBpbmZyYXN0cnVjdHVyZSB0eXBlLCB0aGUgdG90YWwgYWRqdXN0ZWQgcm9hZCBsZW5ndGggZm9yIGEgZ2l2ZW4geWVhciBpcyBleHByZXNzZWQgYmVsb3c6CgokJApsZW5ndGhfe3llYXIsdHlwZX1ee2luc3RhbGx9ICsgbGVuZ3RoX3t5ZWFyLHR5cGV9XntjaGFuZ2VfaX0gLSBsZW5ndGhfe3llYXIsdHlwZX1ee3JlcGxhY2VtZW50X2l9CiQkIFdoZXJlOgoKLSAgICRsZW5ndGhfe3llYXIsdHlwZX1ee2luc3RhbGx9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIGluc3RhbGxhdGlvbgotICAgJGxlbmd0aF97eWVhcix0eXBlfV57Y2hhbmdlX2l9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIGNoYW5nZSBpbiBvcmRlciAkaSQKLSAgICRsZW5ndGhfe3llYXIsdHlwZX1ee3JlcGxhY2VtZW50X2l9JCBhcmUgdGhlIHllYXJseSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBmb3IgYW4gaW5mcmFzdHJ1Y3R1cmUgJHR5cGUkIHJlcGxhY2VkIGJ5IGNoYW5nZSBpbiBvcmRlciAkaSQKCmBgYHtyfQoKIycgQ2FsY3VsYXRlIFllYXJseSBBZGp1c3RlZCBSb2FkIExlbmd0aHMgQnkgSW5mcmFzdHJ1Y3R1cmUgVHlwZQojJyAKIycgQ2FsY3VsYXRlcyB0aGUgY3VtdWxhdGl2ZSB5ZWFybHkgYWRqdXN0ZWQgcm9hZCBsZW5ndGhzIGJ5IGluZnJhc3RydWN0dXJlIHR5cGUgYWNjb3VudGluZyBmb3IgaW5zdGFsbGF0aW9ucyBhbmQgc3Vic2VxdWVudCBjaGFuZ2VzLgojJwojJyBAcGFyYW0gZGYgQSBkYXRhLmZyYW1lIHdpdGggdGhyZWUgY29sdW1ucyBjb250YWluaW5nIHRoZSB5ZWFyLCB0eXBlLCBhbmQgcm9hZCBsZW5ndGhzLgojJyBAcGFyYW0geWVhcl9jb2xzIEEgdmVjdG9yIG9mIHRoZSBuYW1lcyAoY2hhcikgb3IgaW5kaWNlcyAoaW50KSBvZiB0aGUgY29sdW1ucyBjb250YWluaW5nIHRoZSB5ZWFycyBvZiBpbnN0YWxsYXRpb25zIGZvbGxvd2VkIGJ5IGluZnJhc3RydWN0dXJlIGNoYW5nZXMgaW4gb3JkZXIuCiMnIEBwYXJhbSB0eXBlX2NvbHMgQSB2ZWN0b3Igb2YgdGhlIG5hbWVzIChjaGFyKSBvciBpbmRpY2VzIChpbnQpIG9mIHRoZSBjb2x1bW5zIGNvbnRhaW5pbmcgdGhlIGluZnJhc3RydWN0dXJlIHR5cGVzIG9mIGluc3RhbGxhdGlvbnMgZm9sbG93ZWQgYnkgaW5mcmFzdHJ1Y3R1cmUgY2hhbmdlcyBpbiBvcmRlci4KIycgQHBhcmFtIHR5cGVfY29sIFRoZSBuYW1lIChjaGFyKSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIHR5cGUuCiMnIEBwYXJhbSBsZW5fY29scyBBIHZlY3RvciBvZiB0aGUgbmFtZXMgKGNoYXIpIG9yIGluZGljZXMgKGludCkgb2YgdGhlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgcm9hZCBsZW5ndGhzIG9mIGluc3RhbGxhdGlvbnMgZm9sbG93ZWQgYnkgaW5mcmFzdHJ1Y3R1cmUgY2hhbmdlcyBpbiBvcmRlci4KIycgQHBhcmFtIG91dF9jb2xzIFRoZSBuYW1lIChjaGFyKSBvZiB0aGUgY29sdW1uIGNvbnRhaW5pbmcgdGhlIGNhbGN1bGF0ZWQgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJyBAcGFyYW0gb3V0X2NvbCBUaGUgbmFtZSAoY2hhcikgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIHllYXJseSBhZGp1c3RlZCByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycgQHBhcmFtIHJlcGxfc3VmZml4IEEgc3VmZml4IChjaGFyKSB0byBhcHBlbmQgdG8gdGhlIGNvbHVtbnMgcmVwcmVzZW50aW5nIHRoZSByb2FkIGxlbmd0aHMgb2YgcmVwbGFjZWQgaW5mcmFzdHJ1Y3R1cmUgdHlwZXMgZnJvbSBjaGFuZ2VzLgojJyBAcGFyYW0gLi4uIEFkZGl0aW9uYWwgYXJndW1lbnRzIHBhc3NlZCB0byBjYWxjX3llYXJseV9sZW4uCiMnIAojJyBAcmV0dXJuIEEgZGF0YS5mcmFtZSB3aXRoIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgY3VtdWxhdGl2ZSByb2FkIGxlbmd0aHMgb2YgaW5zdGFsbGF0aW9ucywgY2hhbmdlcywgYW5kIHJlcGxhY2VtZW50cywgYW5kIGNhbGN1bGF0ZWQgeWVhcmx5IGFkanVzdGVkIHJvYWQgbGVuZ3RocyBieSB0eXBlLgojJyBAZXhwb3J0CiMnCmNhbGNfeWVhcmx5X2Fkal9sZW4gPC0gZnVuY3Rpb24oCgkJZGYsCgkJeWVhcl9jb2xzID0gYygiaW5zdGFsbF95ZWFyIiwgInVwZ3JhZGUxX3llYXIiLCAidXBncmFkZTJfeWVhciIpLAoJCXR5cGVfY29scyA9IGMoImluc3RhbGxfdHlwZSIsICJ1cGdyYWRlMV90eXBlIiwgInVwZ3JhZGUyX3R5cGUiKSwKCQl0eXBlX2NvbCA9ICJ0eXBlIiwKCQlsZW5fY29scyA9ICJzZWdtZW50X2xlbiIsCgkJb3V0X2NvbHMgPSBjKCJpbnN0YWxsX2xlbiIsICJ1cGdyYWRlMV9sZW4iLCAidXBncmFkZTJfbGVuIiksCgkJb3V0X2NvbCA9ICJhZGpfbGVuIiwKCQlyZXBsX3N1ZmZpeCA9ICJfcmVwbGFjZWQiLAoJCS4uLgoJKSB7CgkKCSMgQ29udmVydCBsZW5fY29sIGlmIGNoYXIKCWxlbl9jb2xzIDwtIHJlcChsZW5fY29scywgbGVuZ3RoKHllYXJfY29scykpCgkKCSMgQ2hlY2sgY29scyBzYW1lIHNpemUKCXllYXJfY29sc19uIDwtIGxlbmd0aCh5ZWFyX2NvbHMpCgl0eXBlX2NvbHNfbiA8LSBsZW5ndGgodHlwZV9jb2xzKQoJbGVuX2NvbHNfbiA8LSBsZW5ndGgobGVuX2NvbHMpCglvdXRfY29sc19uIDwtIGxlbmd0aChvdXRfY29scykKCWlmIChsZW5ndGgodW5pcXVlKGMoeWVhcl9jb2xzX24sIHR5cGVfY29sc19uLCBsZW5fY29sc19uLCBvdXRfY29sc19uKSkpICE9IDEpIHsKCQlzdG9wKGdsdWUoCgkJCSJUaGUgYXJndW1lbnRzICd5ZWFyX2NvbHMnICh7eWVhcl9jb2xzX259KSwgJ3R5cGVfY29scycgKHt0eXBlX2NvbHNfbn0pLCAnbGVuX2NvbHMnICh7bGVuX2NvbHNfbn0pLCBhbmQgJ291dF9jb2xzJyAoe291dF9jb2xzX259KSBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aC4iCgkJKSkKCX0KCQoJIyBDYWxjIHllYXJseSBsZW5zIGJ5IGluZnJhIHR5cGUgcGVyIGluc3RhbGwgb3IgY2hhbmdlCglvdXQgPC0gbGlzdCgpCglmb3IgKGkgaW4gMTpsZW5ndGgoeWVhcl9jb2xzKSkgewoJCQoJCSMgR2V0IHllYXIsIHR5cGUsIGFuZCBsZW4gY29scwoJCXljb2wgPC0geWVhcl9jb2xzW1tpXV0KCQl0Y29sIDwtIHR5cGVfY29sc1tbaV1dCgkJbGNvbCA8LSBsZW5fY29sc1tbaV1dCgkJb2NvbCA8LSBvdXRfY29sc1tbaV1dCgkJCgkJIyBDYWxjIHllYXJseSBsZW4gZm9yIGluc3RhbGwgb3IgY2hhbmdlCgkJb3V0IDwtIGFwcGVuZCgKCQkJb3V0LAoJCQljYWxjX3llYXJseV9sZW4oCgkJCQlkZiwKCQkJCXllYXJfY29sID0geWNvbCwKCQkJCXR5cGVfY29sID0gdGNvbCwKCQkJCWxlbl9jb2wgPSBsY29sLAoJCQkJb3V0X2NvbCA9IG9jb2wsCgkJCQkuLi4KCQkJKSAlPiUKCQkJCXJlbmFtZSgKCQkJCQkieWVhciIgOj0gISF5Y29sLAoJCQkJCSJ0eXBlIiA6PSAhIXRjb2wKCQkJCSkgJT4lIGxpc3QKCQkpCgkJCgkJIyBDYWxjIHllYXJseSBsZW4gZm9yIHJlcGxhY2VtZW50CgkJaWYgKGkgPiAxKSB7CgkJCQoJCQkjIEdldCByZXBsIGNvbHMKCQkJdGNvbF9yZXBsIDwtIHR5cGVfY29sc1tbaSAtIDFdXQoJCQlsY29sX3JlcGwgPC0gbGVuX2NvbHNbW2kgLSAxXV0KCQkJCgkJCSMgRmlsdGVyIGZvciByZXBsIHJlY29yZHMgb25seSB3aGVyZSB0eXBlIGlzIG5vdCBlcSB0byBjaGFuZ2UgdHlwZQoJCQlkZl9yZXBsIDwtIGRmICU+JSBmaWx0ZXIoLmRhdGFbW3Rjb2xdXSAhPSAuZGF0YVtbdGNvbF9yZXBsXV0pCgkJCQoJCQkjIENhbGMgcmVwbCBsZW4gaWYgdGhlcmUgYXJlIGFueSBjaGFuZ2VzCgkJCWhhc19jaGFuZ2UgPC0gIWlzLm5hKGRmX3JlcGxbW3Rjb2xdXSkgJT4lIGFsbCAKCQkJaWYgKGhhc19jaGFuZ2UpIHsKCQkJCW91dCA8LSBhcHBlbmQoCgkJCQkJb3V0LAoJCQkJCWNhbGNfeWVhcmx5X2xlbigKCQkJCQkJZGZfcmVwbCwKCQkJCQkJeWVhcl9jb2wgPSB5Y29sLAoJCQkJCQl0eXBlX2NvbCA9IHRjb2xfcmVwbCwKCQkJCQkJbGVuX2NvbCA9IGxjb2xfcmVwbCwKCQkJCQkJb3V0X2NvbCA9IGdsdWUoIntvY29sfXtyZXBsX3N1ZmZpeH0iKSwKCQkJCQkJLi4uCgkJCQkJKSAlPiUKCQkJCQlyZW5hbWUoCgkJCQkJCSJ5ZWFyIiA6PSAhIXljb2wsCgkJCQkJCSJ0eXBlIiA6PSAhIXRjb2xfcmVwbAoJCQkJCSkgJT4lIGxpc3QKCQkJCSkKCQkJfQoJCX0KCX0KCQoJIyBDb21iaW5lIGFsbCBsZW5zIGluIGxpc3QgdG8gc2luZ2xlIGRmCglvdXQgPC0gb3V0ICU+JQoJCXJlZHVjZSgKCQkJbGVmdF9qb2luLCBieSA9IGMoInllYXIiLCAidHlwZSIpCgkJKSAlPiUKCQl1bmdyb3VwKCkKCQoJIyBDcmVhdGUgdGVtcGxhdGUgZm9yIGNoYW5nZSBhbmQgcmVwbCBjb2xzCgljaGFuZ2VfY29scyA8LSBwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSkjIGNoYW5nZSBjb2xzCgljaGFuZ2VfY29scyA8LSBjKGNoYW5nZV9jb2xzLCBwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSwgcmVwbF9zdWZmaXgpKSAjIHJlcGwgY29scwoJY2hhbmdlX2NvbHNfYWRkIDwtIHJlcCgwLCBsZW5ndGgoY2hhbmdlX2NvbHMpKSAjIHNldCBkZWZhdWx0IHZhbHMKCW5hbWVzKGNoYW5nZV9jb2xzX2FkZCkgPC0gY2hhbmdlX2NvbHMKCQoJIyBBZGQgY2hhbmdlIGFuZCByZXBsIGNvbHMgc2V0IHRvIDAgaWYgbm90IHByZXNlbnQKCW91dCA8LSBvdXQgJT4lIGFkZF9jb2x1bW4oCgkJISEhY2hhbmdlX2NvbHNfYWRkW3NldGRpZmYobmFtZXMoY2hhbmdlX2NvbHNfYWRkKSwgbmFtZXMoLikpXQoJKQoJCgkjIFNldCBOQSB0byAwCglvdXQgPC0gb3V0ICU+JSBtdXRhdGUoCgkJYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfnJlcGxhY2VfbmEoLiwgMCkpCgkpCgkKCSMgQ2FsYyB5ZWFybHkgYWRqIGxlbnMgYnkgaW5mcmEgdHlwZQoJb3V0IDwtIG91dCAlPiUKCQltdXRhdGUoICMgYWRkZWQgbGVuIGJ5IGluZnJhIHR5cGVzIGR1ZSB0byBpbnN0YWxsIG9yIGNoYW5nZXMKCQkJISFvdXRfY29sIDo9IHJlZHVjZShhY3Jvc3MoYWxsX29mKG91dF9jb2xzKSksIGArYCkKCQkpICU+JQoJCW11dGF0ZSggIyByZW1vdmVkIGxlbiBieSBpbmZyYSB0eXBlcyBkdWUgdG8gcmVwbGFjZW1lbnRzCgkJCSEhb3V0X2NvbCA6PSAuZGF0YVtbb3V0X2NvbF1dIC0gcmVkdWNlKAoJCQkJYWNyb3NzKGFsbF9vZigKCQkJCQlwYXN0ZTAob3V0X2NvbHNbMjpvdXRfY29sc19uXSwgcmVwbF9zdWZmaXgpCgkJCQkpKSwKCQkJCWAtYAoJCQkpCgkJKQoJCgkjIFJlbmFtZSB0eXBlIGNvbAoJb3V0IDwtIG91dCAlPiUgcmVuYW1lKCEhdHlwZV9jb2wgOj0gdHlwZSkKCXJldHVybihvdXQpCn0KYGBgCgojIyBGdW5jdGlvbiAzOiBQbG90IExlbmd0aHMgYnkgWWVhciBmb3IgR2VuZXJpYyBUeXBlcwoKUGxvdHMgYW4gYXJlYSBjaGFydCBzaG93aW5nIHRoZSBjdW11bGF0aXZlIHJvYWQgbGVuZ3RocyBieSBhIHVzZXItZGVmaW5lZCB0eXBlIGZvciBlYWNoIHllYXIuCgpUaGlzIGlzIGEgZ2VuZXJpYyBmdW5jdGlvbiBmb3IgdXNlci1kZWZpbmVkIHR5cGVzIHN1Y2ggYXMgaW5mcmFzdHJ1Y3R1cmUgb3Igcm9hZCB0eXBlcy4KCmBgYHtyfQoKIycgUGxvdCBZZWFybHkgUm9hZCBMZW5ndGhzIEJ5IFR5cGUKIycgCiMnIENyZWF0ZXMgYW4gYXJlYSBwbG90IG9mIHJvYWQgbGVuZ3RocyBieSBjYXRlZ29yeSB0eXBlcy4KIycKIycgQHBhcmFtIGRmIEEgZGF0YS5mcmFtZSB3aXRoIHRocmVlIGNvbHVtbnMgY29udGFpbmluZyB0aGUgeWVhciwgdHlwZSwgYW5kIHJvYWQgbGVuZ3Rocy4KIycgQHBhcmFtIHRpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHBsb3QuCiMnIEBwYXJhbSB0aXRsZV91bmRlcmxpbmUgU2V0IHRvIFRSVUUgdG8gdW5kZXJsaW5lIHRoZSB0aXRsZS4KIycgQHBhcmFtIHhfdGl0bGUgVGhlIHRpdGxlIChjaGFyKSBvZiB0aGUgeC1heGlzLgojJyBAcGFyYW0geV90aXRsZSBUaGUgdGl0bGUgKGNoYXIpIG9mIHRoZSB5LWF4aXMuCiMnIEBwYXJhbSB5X3N1ZmZpeCBUaGUgc3VmZml4IChjaGFyKSB0byBhZGQgdG8gdGhlIGVuZCBvZiB5IGF4aXMgdmFsdWVzLgojJyBAcGFyYW0gbGVnZW5kX3RpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIGxlZ2VuZC4KIycgQHBhcmFtIGxlZ2VuZCBTZXQgdG8gVFJVRSB0byBpbmNsdWRlIGEgbGVnZW5kLgojJyBAcGFyYW0geWVhcl9jb2wgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBjb2x1bW4gY29udGFpbmluZyB0aGUgeWVhcnMuCiMnIEBwYXJhbSB5ZWFyX21pbiBUaGUgbWluaW11bSB5ZWFyIChpbnQpIHRvIGRpc3BsYXkuCiMnIEBwYXJhbSB5ZWFyX21heCBUaGUgbWF4aW11bSB5ZWFyIChpbnQpIHRvIGRpc3BsYXkuCiMnIEBwYXJhbSB5ZWFyX2ludCBUaGUgeWVhciBpbnRlcnZhbHMgKGludCkgdG8gZGlzcGxheS4gRm9yIGV4YW1wbGUsIDEgZGlzcGxheXMgZXZlcnkgeWVhciwgYW5kIDIgZGlzcGxheXMgZXZlcnkgdHdvIHllYXJzLgojJyBAcGFyYW0gbGVuX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSByb2FkIGxlbmd0aHMuCiMnIEBwYXJhbSB0eXBlX2NvbCBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIGNvbHVtbiBjb250YWluaW5nIHRoZSB0eXBlLgojJyBAcGFyYW0gdHlwZV9maWx0ZXIgQSB2ZWN0b3IgKGNoYXIpIG9mIHR5cGVzIHRvIHJlbW92ZSBmb21yIHRoZSBwbG90LgojJyBAcGFyYW0gdHlwZV9yZWNvZGUgQSBuYW1lZCB2ZWN0b3IgKGNoYXIpIG9mIG5hbWVzIHJlcHJlc2VudGluZyB0eXBlcyBhbmQgdmFsdWVzIHJlcHJlc2VudGluZyB0aGUgdmFsdWVzIHRvIHJlcGxhY2UgZWFjaCB0eXBlIHdpdGguCiMnIEBwYXJhbSBsaW5lXzUwa20gU2V0IHRvIFRSVUUgdG8gZHJhdyB0aGUgNTAga20gcmVkIHJlZmVyZW5jZSBsaW5lLgojJyBAcGFyYW0gbGluZV95ZWFyIFNldCB0byBhIHllYXIgKGludCkgdG8gZHJhdyBhIHJlZmVyZW5jZSBsaW5lIGZvciBhIHllYXIuIElmIEZBTFNFLCBhIGxpbmUgd2lsbCBub3QgYmUgZHJhd24uCiMnIEBwYXJhbSBjb2xvcl9sb3cgVGhlIGJvdHRvbSBjb2xvciAoY2hhcikgb2YgdGhlIHR5cGUuCiMnIEBwYXJhbSBjb2xvcl9oaWdoIFRoZSB0b3AgY29sb3IgKGNoYXIpIG9mIHRoZSB0eXBlLgojJyBAcmV0dXJuIEFuIGFyZWEgZ2dwbG90IG9mIHRoZSBjdW11bGF0aXZlIHllYXJseSByb2FkIGxlbmd0aHMgYnkgdHlwZS4KIycgQGV4cG9ydAojJwpwbG90X3llYXJseV9sZW4gPC0gZnVuY3Rpb24oCiAgICAgICAgZGYsCiAgICAgICAgdGl0bGUgPSAiIiwKICAgICAgICB0aXRsZV91bmRlcmxpbmUgPSBUUlVFLAogICAgICAgIHhfdGl0bGUgPSAiIiwKICAgICAgICB5X3RpdGxlID0gIiIsCiAgICAgICAgeV9zdWZmaXggPSAiIGttIiwKICAgICAgICBsZWdlbmRfdGl0bGUgPSAiVHlwZSIsCiAgICAgICAgbGVnZW5kID0gVFJVRSwKICAgICAgICB5ZWFyX2NvbCA9ICJ5ZWFyIiwKICAgICAgICB5ZWFyX21pbiA9IEZBTFNFLAogICAgICAgIHllYXJfbWF4ID0gRkFMU0UsCiAgICAgICAgeWVhcl9pbnQgPSAxLAogICAgICAgIGxlbl9jb2wgPSAiYWRqX2xlbiIsCiAgICAgICAgdHlwZV9jb2wgPSAidHlwZSIsCiAgICAgICAgdHlwZV9maWx0ZXIgPSBjKCksCiAgICAgICAgdHlwZV9yZWNvZGUgPSBjKCksCiAgICAgICAgbGluZV81MGttID0gRkFMU0UsCiAgICAgICAgbGluZV95ZWFyID0gRkFMU0UsCiAgICAgICAgY29sb3JfbG93ID0gIiNERkVCRjciLAogICAgICAgIGNvbG9yX2hpZ2ggPSAiIzM2ODNCQiIKKSB7CgkKCSMgRmlsdGVyIHRvIHN0YXJ0IGFuZCBlbmQgeWVhcnMKCWlmICh5ZWFyX21pbiA+IDApIHsKCQlkZiA8LSBkZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2xdXSA+PSB5ZWFyX21pbgoJCSkKCX0KCWlmICh5ZWFyX21heCA+IDApIHsKCQlkZiA8LSBkZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2xdXSA8PSB5ZWFyX21heAoJCSkKCX0KCQoJIyBGaWx0ZXIgb3V0IHBhcnRpY3VsYXIgaW5mcmFzdHJ1Y3R1cmUgdHlwZXMKCWlmIChsZW5ndGgodHlwZV9maWx0ZXIpID4gMCkgewoJCWRmIDwtIGRmICU+JSBmaWx0ZXIoCgkJCSEuZGF0YVtbdHlwZV9jb2xdXSAlaW4lIHR5cGVfZmlsdGVyCgkJKQoJfQoJCgkjIFJlY29kZSBhbmQgcmVvcmRlciBjYXRlZ29yeSB0eXBlcwoJaWYgKGxlbmd0aCh0eXBlX3JlY29kZSkgPiAwKSB7CgkJCgkJIyBSZW9yZGVyIGNhdGVnb3J5IHR5cGVzCgkJdHlwZV91bmlxIDwtIHVuaXF1ZShkZltbdHlwZV9jb2xdXSkKCQl0eXBlX3Jlb3JkZXIgPC0gbmFtZXModHlwZV9yZWNvZGUpCgkJdHlwZV9yZW9yZGVyIDwtIGModHlwZV9yZW9yZGVyLCB0eXBlX3VuaXFbIXR5cGVfdW5pcSAlaW4lIHR5cGVfcmVvcmRlcl0pCgkJZGZbW3R5cGVfY29sXV0gPC0gZmFjdG9yKGRmW1t0eXBlX2NvbF1dLCBsZXZlbHMgPSB0eXBlX3Jlb3JkZXIpCgkJCgkJIyBSZWNvZGUgY2F0ZWdvcnkgdHlwZXMKCQlkZltbdHlwZV9jb2xdXSA8LSByZWNvZGUoZGZbW3R5cGVfY29sXV0sICEhIXR5cGVfcmVjb2RlKQoJfQoJCgkjIENyZWF0ZSBmaWxsIGNvbG9ycwoJdHlwZV9uIDwtIGxlbmd0aCh0eXBlX3VuaXEpCgl0eXBlX2NvbG9ycyA8LSBzY2FsZXM6OnNlcV9ncmFkaWVudF9wYWwoCgkJY29sb3JfbG93LAoJCWNvbG9yX2hpZ2gKCSkoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSB0eXBlX24pKQoJCgkjIENyZWF0ZSBiYXNlIGFyZWEgcGxvdCB3aXRoIGxlZ2VuZCBhbmQgbGFiZWxzCglsZW5fbWF4IDwtIG1heChkZltbbGVuX2NvbF1dLCBuYS5ybSA9IFRSVUUpCgl5ZWFyX21heCA8LSBtYXgoZGZbW3llYXJfY29sXV0sIG5hLnJtID0gVFJVRSkKCW91dCA8LSBnZ3Bsb3QoCgkJZGYsCgkJYWVzKAoJCQl4ID0gLmRhdGFbW3llYXJfY29sXV0sCgkJCXkgPSAuZGF0YVtbbGVuX2NvbF1dLAoJCQlmaWxsID0gLmRhdGFbW3R5cGVfY29sXV0sCgkJCW9yZGVyID0gZGVzYyguZGF0YVtbdHlwZV9jb2xdXSkKCQkpCgkpICsKCWdlb21fYXJlYShjb2xvdXIgPSBOQSwgYWxwaGEgPSAwLjcpICsKCXNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHR5cGVfY29sb3JzKSArCglnZW9tX2xpbmUoCgkJcG9zaXRpb24gPSAic3RhY2siLAoJCXNpemUgPSAwLjIKCSkgKwoJbGFicygKCQl4ID0geF90aXRsZSwKCQl5ID0geV90aXRsZSwKCQlmaWxsID0gbGVnZW5kX3RpdGxlCgkpICsKCWd1aWRlcygKCQlmaWxsID0gRkFMU0UsCgkJY29sb3IgPSBGQUxTRQoJKSArCglzY2FsZV94X2NvbnRpbnVvdXMoCgkJYnJlYWtzID0gc2VxKHllYXJfbWluLCB5ZWFyX21heCwgYnkgPSB5ZWFyX2ludCksCgkJbGFiZWxzID0gc2VxKHllYXJfbWluLCB5ZWFyX21heCwgYnkgPSB5ZWFyX2ludCksCgkJbGltaXRzID0gYyh5ZWFyX21pbiwgeWVhcl9tYXgpCgkpICsKCXNjYWxlX3lfY29udGludW91cygKCQlsYWJlbCA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9IHlfc3VmZml4KQoJKSArCgl0aGVtZV9taW5pbWFsKCkgKwoJdGhlbWUoCgkJcGxvdC5tYXJnaW4gPSB1bml0KGMoNSw1LDUsNSksICJwb2ludHMiKQoJKQoJCgkjIEFkZCB0aXRsZQoJaWYgKHRpdGxlX3VuZGVybGluZSkgewoJCW91dCA8LSBvdXQgKyBnZ3RpdGxlKAoJCQlicXVvdGUodW5kZXJsaW5lKC4odGl0bGUpKSkKCQkpCgl9IGVsc2UgewoJCW91dCA8LSBvdXQgKyBnZ3RpdGxlKHRpdGxlKQoJfQoJCgkjIEFkZCBsZWdlbmQKCWlmIChsZWdlbmQpIHsKCQlvdXQgPC0gb3V0ICsgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQoCgkJCXJldmVyc2UgPSBGQUxTRSwKCQkJb3ZlcnJpZGUuYWVzID0gbGlzdCgKCQkJCWFscGhhID0gMC43LAoJCQkJY29sb3IgPSBOQSwKCQkJCXNoYXBlID0gTkEKCQkJKQoJCSkpCgl9CgkKCSMgQWRkIGRvdHRlZCB5ZWFyIHJlZiBsaW5lCglpZiAobGluZV95ZWFyKSB7CgkJb3V0IDwtIG91dCArIGdlb21fdmxpbmUoCgkJCXhpbnRlcmNlcHQgPSBsaW5lX3llYXIsCgkJCWNvbG9yID0gImJsYWNrIiwKCQkJbGluZXR5cGUgPSAiZGFzaGVkIgoJCSkKCX0KCQoJIyBBZGQgcmVkIDUwa20gcmVmIGxpbmUKCWlmIChsaW5lXzUwa20pIHsKCQlvdXQgPC0gb3V0ICsgZ2VvbV9zZWdtZW50KCAjIDUwa20gcmVkIGxpbmUKCQkJYWVzKAoJCQkJeCA9IDIwMDksCgkJCQl5ID0gMCwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IDUwLAoJCQkJY29sb3IgPSAiI2JiMDAwMCIsCgkJCQloanVzdCA9IDAuMTUKCQkJKQoJCSkgKwoJCWdlb21fc2VnbWVudCggIyA1MGttIHJlZCB0cmlhbmdsZSBwb2ludCBkb3duCgkJCWFlcygKCQkJCXggPSAyMDA5LAoJCQkJeSA9IDUwLjAxIC0gKGxlbl9tYXggKiAwLjA1KSwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IDUwIC0gKGxlbl9tYXggKiAwLjA1KSwKCQkJCWNvbG9yID0gIiNiYjAwMDAiLAoJCQkJaGp1c3QgPSAwLjE1CgkJCSksCgkJCWFycm93ID0gYXJyb3coCgkJCQlsZW5ndGggPSB1bml0KDAuMDMsICJucGMiKSwKCQkJCWVuZHMgPSAibGFzdCIsCgkJCQl0eXBlID0gImNsb3NlZCIKCQkJKQoJCSkgKwoJCWdlb21fc2VnbWVudCggIyA1MGttIHJlZCB0cmlhbmdsZSBwb2ludCB1cAoJCQlhZXMoCgkJCQl4ID0gMjAwOSwKCQkJCXkgPSAobGVuX21heCAqIDAuMDUpIC0gMC4wMSwKCQkJCXhlbmQgPSAyMDA5LAoJCQkJeWVuZCA9IChsZW5fbWF4ICogMC4wNSksCgkJCQljb2xvciA9ICIjYmIwMDAwIiwKCQkJCWhqdXN0ID0gMC4xNQoJCQkpLAoJCQlhcnJvdyA9IGFycm93KAoJCQkJbGVuZ3RoID0gdW5pdCgwLjAzLCAibnBjIiksCgkJCQllbmRzID0gImxhc3QiLAoJCQkJdHlwZSA9ICJjbG9zZWQiCgkJCSkKCQkpICsKCQlhbm5vdGF0ZSgKCQkJInRleHQiLAoJCQl4ID0gMjAwOSwKCQkJeSA9IDUwLAoJCQlsYWJlbCA9ICI1MGttIiwKCQkJY29sb3IgPSAiI2JiMDAwMCIsCgkJCWhqdXN0ID0gLTAuMjI1CgkJKQoJfQoJcmV0dXJuKG91dCkKfQpgYGAKCiMjIyBGdW5jdGlvbiAzLjE6IFBsb3QgTGVuZ3RocyBieSBZZWFyIGZvciBJbmZyYXN0cnVjdHVyZSBUeXBlcwoKUGxvdHMgYXJlYSBjaGFydHMgb2YgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlcyBmb3IgYSBsaXN0IG9mIGRhdGEuCgpUaGlzIHVzZXMgdGhlIGBwbG90X3llYXJseV9sZW5gIGZ1bmN0aW9uLgoKYGBge3J9CgojJyBQbG90IFllYXJseSBSb2FkIExlbmd0aHMgQnkgSW5mcmFzdHJ1Y3R1cmUgVHlwZQojJyAKIycgQ3JlYXRlcyBhcmVhIHBsb3RzIG9mIHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlLgojJwojJyBAcGFyYW0gZGZfbGlzdCBBIGxpc3Qgb2YgZGF0YS5mcmFtZSBjb250YWluaW5nIHRoZSBpbnN0YWxsIGFuZCBjaGFuZ2UgeWVhcnMsIHR5cGUsIGFuZCByb2FkIHNlZ21lbnQgbGVuZ3Rocy4KIycgQHJldHVybiBNdWx0aXBsZSBhcmVhIGdncGxvdHMgb2YgdGhlIGN1bXVsYXRpdmUgeWVhcmx5IHJvYWQgbGVuZ3RocyBieSBpbmZyYXN0cnVjdHVyZSB0eXBlIGNvbWJpbmVkIHdpdGggcGF0Y2h3b3JrLgojJyBAZXhwb3J0CiMnCnBsb3RfeWVhcmx5X2xlbl9pbmZyYSA8LSBmdW5jdGlvbihkZl9saXN0KSB7CgkKCSMgQ3JlYXRlIGluZnJhIHBsb3RzIGZyb20gZGF0YQoJcCA8LSBsaXN0KCkKCWZvciAoaSBpbiAxOmxlbmd0aChkZl9saXN0KSkgewoJCQoJCSMgR2V0IGRhdGEgYW5kIHBsb3QgdGl0bGUKCQlkZiA8LSBkZl9saXN0W1tpXV0KCQlwdGl0bGUgPC0gbmFtZXMoZGZfbGlzdClbW2ldXQoJCQoJCSMgQ3JlYXRlIGFuZCBhZGQgaW5mcmEgcGxvdCB0byBsaXN0CgkJcFtbaV1dIDwtIGNhbGNfeWVhcmx5X2Fkal9sZW4oZGYsIHR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfaW5mcmEpICU+JQoJCSAgICBwbG90X3llYXJseV9sZW4oCgkJICAgIAl0aXRsZSA9IHB0aXRsZSwKCQkgICAgCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJICAgICAgICB5ZWFyX21heCA9IHNldHRpbmdzJHllYXJfbWF4LAoJCQkJdHlwZV9jb2wgPSBzZXR0aW5ncyR0eXBlX2NvbF9pbmZyYSwKCQkJCXR5cGVfZmlsdGVyID0gc2V0dGluZ3MkdHlwZV9maWx0ZXJfaW5mcmEsCgkJICAgICAgICB0eXBlX3JlY29kZSA9IHNldHRpbmdzJHR5cGVfcmVjb2RlX2luZnJhLAoJCQkJbGVnZW5kX3RpdGxlID0gIkluZnJhc3RydWN0dXJlIFR5cGUiLAoJCQkJbGluZV81MGttID0gVFJVRSwKCQkgICAgICAgIGxpbmVfeWVhciA9IHNldHRpbmdzJGxpbmVfeWVhcgoJCSAgICApCgl9CgkKCSMgWS1heGlzIHRpdGxlCgl5X3RpdGxlIDwtIGdncGxvdCgpICsKCQlhbm5vdGF0ZSgKCQkJZ2VvbSA9ICJ0ZXh0IiwKCQkJeCA9IDEsCgkJCXkgPSAxLAoJCQlsYWJlbCA9ICJUb3RhbCBMZW5ndGggKENlbnRyZWxpbmUga20pIiwKCQkJYW5nbGUgPSA5MCwKCQkJc2l6ZSA9IDUKCQkpICsKCQljb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSsKCQl0aGVtZV92b2lkKCkKCQoJIyBDb21iaW5lIGFsbCBpbmZyYSBwbG90cyB0b2dldGhlcgoJb3V0IDwtICh5X3RpdGxlIHwgd3JhcF9wbG90cyhwLCBucm93ID0gbGVuZ3RoKHApKSkgKwoJCXBsb3RfYW5ub3RhdGlvbigKCQkJdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSIsCgkJCWNhcHRpb24gPSBzcHJpbnRmKCJZZWFycyAoJXMtJXMpIiwgc2V0dGluZ3MkeWVhcl9taW4sIHNldHRpbmdzJHllYXJfbWF4KSwKCQkJdGhlbWUgPSB0aGVtZSgKCQkJCXBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiksCgkJCQlwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNCkKCQkJKQoJCSkgKwoJCXBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMC4wNSwgMSkpCglyZXR1cm4ob3V0KQp9CmBgYAoKIyMjIEZ1bmN0aW9uIDMuMjogUGxvdCBMZW5ndGhzIGJ5IFllYXIgZm9yIFJvYWQgVHlwZXMKClBsb3RzIGFyZWEgY2hhcnRzIG9mIHllYXJseSByb2FkIGxlbmd0aHMgYnkgb3ZlcmFsbCByb2FkIHR5cGUgYW5kIGJ5IGluZnJhc3RydWN0dXJlIHNlcGFyYXRlZCBieSBlYWNoIHJvYWQgdHlwZS4KClRoaXMgdXNlcyB0aGUgYHBsb3RfeWVhcmx5X2xlbmAgZnVuY3Rpb24uCgpgYGB7cn0KCiMnIFBsb3QgWWVhcmx5IFJvYWQgTGVuZ3RocyBCeSBSb2FkIFR5cGUKIycKIycgQ3JlYXRlcyBhcmVhIHBsb3RzIG9mIHJvYWQgbGVuZ3RocyBieSBvdmVyYWxsIHJvYWQgdHlwZSwgYW5kIGJ5IGluZnJhc3RydWN0dXJlIHBlciByb2FkIHR5cGUuCiMnCiMnIEBwYXJhbSBkZiBUaGUgZGF0YS5mcmFtZSBjb250YWluaW5nIHRoZSBpbnN0YWxsIGFuZCBjaGFuZ2UgeWVhcnMsIHR5cGUsIGFuZCByb2FkIHNlZ21lbnQgdHlwZXMgYW5kIGxlbmd0aHMuIAojJyBAcmV0dXJuIE11bHRpcGxlIGFyZWEgZ2dwbG90cyBvZiB0aGUgY3VtdWxhdGl2ZSB5ZWFybHkgcm9hZCBsZW5ndGhzIGJ5IHJvYWQgdHlwZSBjb21iaW5lZCB3aXRoIHBhdGNod29yay4KIycgQGV4cG9ydAojJwpwbG90X3llYXJseV9sZW5fcm9hZCA8LSBmdW5jdGlvbihkZiwgdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSIpIHsKCQoJIyBDcmVhdGUgbGlzdCB0byBzdG9yZSBwbG90cwoJcCA8LSBsaXN0KCkKCgkjIFBsb3Qgb3ZlcmFsbCByb2FkIHR5cGVzCglwW1sxXV0gPC0gY2FsY195ZWFybHlfbGVuKAoJCWRmLAoJCXllYXJfY29sID0gc2V0dGluZ3MkeWVhcl9jb2xfcm9hZCwKCQl0eXBlX2NvbCA9IHNldHRpbmdzJHR5cGVfY29sX3JvYWQKCSkgJT4lCgkgICAgcGxvdF95ZWFybHlfbGVuKAoJICAgIAl0aXRsZSA9IHRpdGxlLAoJICAgIAl0aXRsZV91bmRlcmxpbmUgPSBGQUxTRSwKCSAgICAJeWVhcl9jb2wgPSBzZXR0aW5ncyR5ZWFyX2NvbF9yb2FkLAoJCQl5ZWFyX21pbiA9IHNldHRpbmdzJHllYXJfbWluLAoJICAgICAgICB5ZWFyX21heCA9IHNldHRpbmdzJHllYXJfbWF4LAoJCQl4X3RpdGxlID0gc3ByaW50ZigiWWVhcnMgKCVzLSVzKSIsIHNldHRpbmdzJHllYXJfbWluLCBzZXR0aW5ncyR5ZWFyX21heCksCgkJCXlfdGl0bGUgPSAiVG90YWwgTGVuZ3RoIChDZW50cmVsaW5lIGttKSIsCgkJCWxlZ2VuZF90aXRsZSA9ICJSb2Fkd2F5IFR5cGUiLAoJCQl0eXBlX2NvbCA9IHNldHRpbmdzJHR5cGVfY29sX3JvYWQsCgkJCXR5cGVfcmVjb2RlID0gc2V0dGluZ3MkdHlwZV9yZWNvZGVfcm9hZCwKCQkJbGVuX2NvbCA9ICJsZW4iLAoJCQlsaW5lXzUwa20gPSBGQUxTRSwKCQkJbGluZV95ZWFyID0gc2V0dGluZ3MkbGluZV95ZWFyLAoJICAgICAgICBjb2xvcl9sb3cgPSAiI0MxRERCMyIsCgkgICAgICAgIGNvbG9yX2hpZ2ggPSAiIzI5N0EyMiIKCSAgICApICsKCQl0aGVtZSgKCQkJcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLAoJCQlwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCAwLCAicHQiKQoJCSkKCQoJIyBQbG90IGFydGVyaWFsLCBjb2xsZWN0b3IsIGFuZCBsb2NhbCByb2FkIGJ5IGluZnJhCglydHlwZXMgPC0gYygiQXJ0ZXJpYWwiLCAiQ29sbGVjdG9yIiwgIkxvY2FsIikKCWZvciAoaSBpbiAxOmxlbmd0aChydHlwZXMpKSB7CgkJCgkJIyBHZXQgcm9hZCB0eXBlCgkJciA8LSBydHlwZXNbaV0KCQkKCQkjIENyZWF0ZSBpbmZyYSBwbG90IGZvciByb2FkIHR5cGUKCQlwW1tpICsgMV1dIDwtIGNhbGNfeWVhcmx5X2Fkal9sZW4oCgkJCWRmICU+JSBmaWx0ZXIocm9hZF90eXBlID09IHIpLAoJCQl0eXBlX2NvbCA9IHNldHRpbmdzJHR5cGVfY29sX2luZnJhCgkJKSAlPiUKCQkJcGxvdF95ZWFybHlfbGVuKAoJCQkgICAgdGl0bGUgPSBzcHJpbnRmKCIlcyBSb2Fkd2F5cyIsIHIpLAoJCQkgICAgdGl0bGVfdW5kZXJsaW5lID0gRkFMU0UsCgkJCSAgICBsaW5lXzUwa20gPSBGQUxTRSwKCQkJICAgIGxpbmVfeWVhciA9IHNldHRpbmdzJGxpbmVfeWVhciwKCQkJICAgIHllYXJfaW50ID0gMiwKCQkJICAgIHhfdGl0bGUgPSBzcHJpbnRmKCJZZWFycyAoJXMtJXMpIiwgc2V0dGluZ3MkeWVhcl9taW4sIHNldHRpbmdzJHllYXJfbWF4KSwKCQkJICAgIHlfdGl0bGUgPSAiVG90YWwgTGVuZ3RoIChDZW50cmVsaW5lIGttKSIsCgkJCSAgICB5ZWFyX21pbiA9IHNldHRpbmdzJHllYXJfbWluLAoJCSAgICAgICAgeWVhcl9tYXggPSBzZXR0aW5ncyR5ZWFyX21heCwKCQkJCXR5cGVfY29sID0gc2V0dGluZ3MkdHlwZV9jb2xfaW5mcmEsCgkJCQl0eXBlX2ZpbHRlciA9IHNldHRpbmdzJHR5cGVfZmlsdGVyX2luZnJhLAoJCSAgICAgICAgdHlwZV9yZWNvZGUgPSBzZXR0aW5ncyR0eXBlX3JlY29kZV9pbmZyYSwKCQkJCWxlZ2VuZF90aXRsZSA9ICJJbmZyYXN0cnVjdHVyZSBUeXBlIgoJCQkpICsKCQkJdGhlbWUoCgkJCQlwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCgkJCQlwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAxMiwgMCwgMCwgInB0IikKCQkJKQoJfQoJCgkjIFBsb3QgaG9yaXpvbnRhbCBncmFkaWVudCBiYXIKCWdyYWRfYmFyIDwtICBnZ3Bsb3QoZGF0YS5mcmFtZSh4ID0gMTo0KSwgYWVzKHggPSB4LCB5ID0gMSwgY29sb3IgPSB4KSkgKwoJCWdlb21fbGluZShzaXplID0gNCkgKwoJCXNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICIjQzFEREIzIiwgaGlnaCA9ICIjMjk3QTIyIikgKwoJCXRoZW1lX3ZvaWQoKSArCgkJZ3VpZGVzKGNvbG9yID0gRkFMU0UpICsKCQl0aGVtZSgKCQkJYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKCSAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAoJICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAoJICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCgkJCXBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDAsICJwdCIpCgkJKQoJCgkjIFBsb3Qgb3ZlcmFsbCBhbmQgcm9hZCB0eXBlIHBsb3RzIHRvZ2V0aGVyCglvdXQgPC0gKCAjIG92ZXJhbGwgcGxvdAoJCXBsb3Rfc3BhY2VyKCkgKwoJCXBbWzFdXSArCgkJcGxvdF9zcGFjZXIoKSArCgkJcGxvdF9sYXlvdXQoCgkJCXdpZHRocyA9IGMoMC4yNSwgMC4zNSwgMC4yKQoJCSkKCSkgLyAoICMgZ3JhZGllbnQgYmFyCgkJcGxvdF9zcGFjZXIoKSArCgkJZ3JhZF9iYXIgKwoJCXBsb3Rfc3BhY2VyKCkgKwoJCXBsb3RfbGF5b3V0KHdpZHRocyA9IGMoLTAuOCwgMTAsIC0xLjEpKQoJKSAvICggIyBpbmZyYSBwbG90cwoJCXBbWzJdXSArCgkJcFtbM11dICsKCQlwW1s0XV0KCSkgKyBwbG90X2xheW91dCgKCQloZWlnaHRzID0gYygxMiwgMSwgOCkKCSkgKyBwbG90X2Fubm90YXRpb24oICMgQSBCIHRhZ3MKCQl0YWdfbGV2ZWxzID0gbGlzdChjKCJBIiwgIiIsICJCIiwgIiIsICIiKSkKCSkgJiB0aGVtZSgKCQlwbG90LnRhZyA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTIpCgkpCglyZXR1cm4ob3V0KQp9CmBgYAoKIyMgRnVuY3Rpb24gNDogUGxvdCBZZWFybHkgRGlmZmVyZW5jZXMKClBsb3RzIGEgYmFyIGNoYXJ0IG9mIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIGNvbHVtbnMgY29udGFpbmluZyB5ZWFycy4KClRoaXMgZnVuY3Rpb24gaXMgdXNlZCB0byBjaGVjayB0aGUgZGlmZmVyZW5jZXMgaW4gaW5zdGFsbGF0aW9uIHllYXJzIGJldHdlZW4gdGhlIGNpdHkncyBkYXRhIGFuZCB0aGUgdmVyaWZpZWQgZGF0YS4KCmBgYHtyfQojJyBQbG90IFllYXJseSBEaWZmZXJlbmNlcwojJwojJyBDcmVhdGVzIGEgYmFyIHBsb3Qgb2YgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIHllYXJzLgojJwojJyBAcGFyYW0gZGYgVGhlIGRhdGEuZnJhbWUgY29udGFpbmluZyB0aGUgdHdvIGNvbHVtbnMgd2l0aCB0aGUgeWVhcnMuCiMnIEBwYXJhbSB5ZWFyX2NvbDEgVGhlIG5hbWUgKGNoYXIpIG9yIGluZGV4IChpbnQpIG9mIHRoZSBmaXJzdCB5ZWFyIGNvbHVtbi4KIycgQHBhcmFtIHllYXJfY29sMiBUaGUgbmFtZSAoY2hhcikgb3IgaW5kZXggKGludCkgb2YgdGhlIHNlY29uZCB5ZWFyIGNvbHVtbiB0byBiZSBzdWJ0cmFjdGVkIGZyb20uCiMnIEBwYXJhbSB5ZWFyX2NvbDFfbmFtZSBUaGUgbmFtZSBhbGlhcyAoY2hhcikgb2YgdGhlIGZpcnN0IHllYXIgY29sdW1uIHllYXJfY29sMS4KIycgQHBhcmFtIHllYXJfY29sMl9uYW1lIFRoZSBuYW1lIGFsaWFzIChjaGFyKSBvZiB0aGUgc2Vjb25kIHllYXIgY29sdW1uIHllYXJfY29sMi4KIycgQHBhcmFtIHllYXJfbWluIFRoZSBtaW5pbXVtIHllYXIgKGludCkgdG8gY2FsY3VsYXRlIGRpZmZlcmVuY2VzIGZvci4KIycgQHBhcmFtIHllYXJfbWF4IFRoZSBtYXhpbXVtIHllYXIgKGludCkgdG8gY2FsY3VsYXRlIGRpZmZlcmVuY2VzIGZvci4KIycgQHBhcmFtIHRpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHBsb3QuCiMnIEBwYXJhbSB0aXRsZV9uIFNldCB0byBUUlVFIHRvIGFkZCB0aGUgbnVtYmVyIG9mIHRvdGFsIHNlZ21lbnRzIGNvbnNpZGVyZWQuCiMnIEBwYXJhbSB4X3RpdGxlIFRoZSB0aXRsZSAoY2hhcikgb2YgdGhlIHgtYXhpcy4KIycgQHBhcmFtIHlfdGl0bGUgVGhlIHRpdGxlIChjaGFyKSBvZiB0aGUgeS1heGlzLgojJyBAcGFyYW0geF9icmVha3MgVGhlIG51bWJlciAoaW50KSBvZiBicmVha3MgdG8gc2hvdyBvbiB0aGUgeC1heGlzLiBTZXQgdG8gRkFMU0UgdG8gbGV0IGdncGxvdCBhdXRvbWF0aWNhbGx5IGRlY2lkZS4KIycgQHJldHVybiBBIGdncGxvdCBvZiB5ZWFybHkgZGlmZmVyZW5jZXMgKHllYXJfY29sMiAtIHllYXJfY29sMSksIGRpc3BsYXlpbmcgdGhlIHByb3BvcnRpb24gb2Ygcm93cyBmb3IgZWFjaCBkaWZmZXJlbmNlIGluIHllYXJzLgojJyBAZXhwb3J0CiMnCnBsb3RfeWVhcmx5X2RpZmYgPC0gZnVuY3Rpb24oCgkJZGYsCgkJeWVhcl9jb2wxID0gImluc3RhbGxfeWVhcl9vcmlnIiwKCQl5ZWFyX2NvbDIgPSAiaW5zdGFsbF95ZWFyIiwKCQl5ZWFyX2NvbDFfbmFtZSA9ICJDaXR5IFllYXIiLAoJCXllYXJfY29sMl9uYW1lID0gIlZlcmlmaWVkIFllYXIiLAoJCXllYXJfbWluID0gc2V0dGluZ3MkeWVhcl9taW4sCgkJeWVhcl9tYXggPSBzZXR0aW5ncyR5ZWFyX21heCwKCQl0aXRsZSA9IHNwcmludGYoCgkJCSJEaWZmZXJlbmNlIGluIFllYXJzLCBDb21wYXJpbmcgJXMgYW5kICVzIiwKCQkJeWVhcl9jb2wxX25hbWUsCgkJCXllYXJfY29sMl9uYW1lCgkJKSwKCQl0aXRsZV9uID0gVFJVRSwKCQl4X3RpdGxlID0gc3ByaW50ZigKCQkJIkRpZmZlcmVuY2UgaW4gWWVhcnMgKCVzIC0gJXMpIiwKCQkJeWVhcl9jb2wyX25hbWUsCgkJCXllYXJfY29sMV9uYW1lCgkJKSwKCQl5X3RpdGxlID0gIlByb3BvcnRpb24gb2YgVG90YWwgU2VnbWVudHMiLAoJCXhfYnJlYWtzID0gMTUKKSB7Cgl5ZGlmZiA8LSBkZgoJCgkjIEZpbHRlciB3aXRoaW4gbWluIHllYXIKCWlmICh5ZWFyX21pbikgewoJCXlkaWZmIDwtIHlkaWZmICU+JSBmaWx0ZXIoCgkJCS5kYXRhW1t5ZWFyX2NvbDFdXSA+PSB5ZWFyX21pbiB8IC5kYXRhW1t5ZWFyX2NvbDJdXSA+PSB5ZWFyX21pbgoJCSkKCX0KCQoJIyBGaWx0ZXIgd2l0aGluIG1heCB5ZWFyCglpZiAoeWVhcl9tYXgpIHsKCQl5ZGlmZiA8LSB5ZGlmZiAlPiUgZmlsdGVyKAoJCQkuZGF0YVtbeWVhcl9jb2wxXV0gPD0geWVhcl9tYXggfCAuZGF0YVtbeWVhcl9jb2wyXV0gPD0geWVhcl9tYXgKCQkpCgl9CgkKCSMgQWRkIG4gdG8gdGl0bGUKCWlmICh0aXRsZV9uKSB7CgkJdGl0bGUgPC0gc3ByaW50ZigiJXMgKG49JXMpIiwgdGl0bGUsIG5yb3coeWRpZmYpKQoJfQoJCgkjIENhbGMgeWVhcmx5IGRpZmYKCXlkaWZmIDwtIHlkaWZmICU+JQoJCW11dGF0ZSh5ZWFyX2RpZmYgPSBpbnN0YWxsX3llYXIgLSBpbnN0YWxsX3llYXJfb3JpZykgJT4lCgkJY291bnQoeWVhcl9kaWZmKQoJCgkjIFBsb3QgeWVhbHkgZGlmZnMKCW91dCA8LSB5ZGlmZiAlPiUgCgkJZ2dwbG90KGFlcygKCQkJeCA9IHllYXJfZGlmZiwKCQkJeSA9IChuIC8gc3VtKG4pKSAqIDEwMAoJCSkpICsKCQlnZW9tX2JhcigKCQkJc3RhdCA9ICJpZGVudGl0eSIsCgkJCWNvbG9yID0gIiMzMzJhOTQiLAoJCQlmaWxsID0gIiNjM2Q1ZTQiLAoJCQl3aWR0aCA9IDEKCQkpICsKCQlsYWJzKAoJCQl0aXRsZSA9IHRpdGxlLAoJCQl4ID0geF90aXRsZSwKCQkJeSA9IHlfdGl0bGUKCQkpICsKCQlzY2FsZV95X2NvbnRpbnVvdXMoCgkJCWxhYmVsID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc3VmZml4ID0gIiUiKQoJCSkgKwoJCXRoZW1lKAoJCQlwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikKCQkpCgkKCSMgU2V0IHggaW50ZXJ2YWwgYnJlYWtzCglpZiAoeF9icmVha3MpIHsKCQlvdXQgPC0gb3V0ICsgc2NhbGVfeF9jb250aW51b3VzKAoJCQlicmVha3MgPSBzY2FsZXM6OmJyZWFrc19wcmV0dHkoeF9icmVha3MpCgkJKQoJfQoJcmV0dXJuKG91dCkKfQoKYGBgCgojIERhdGEKCkxvYWQgcmF3IGRhdGEgcHJvdmlkZWQgYnkgS29ucmFkIFNhbXNlbC4KCiMjIFZhbmNvdXZlciBSYXcgRGF0YQoKYGBge3J9CgojIExvYWQgcmF3IGRhdGEKdmFuY19iaWtld2F5cyA8LSByZWFkX2NzdigiLi4vZGF0YS92YW5jb3V2ZXJfYmlrZXdheXNfMjAwOV8yMDIyX3YxLmNzdiIpCnZhbmNfcm9hZHMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvdmFuY291dmVyX3JvYWRzXzIwMDlfMjAyMl92MS5jc3YiKQoKIyBDb21iaW5lIHJhdyBkYXRhCnZhbmMgPC0gdmFuY19iaWtld2F5cyAlPiUKCXNlbGVjdCgKCQlJRF9EQVRBRU5UUlksCgkJSU5TVF9ZUl9PUklHLAoJCUlOU1RfWVIsCgkJSU5TVF9NSU5fSFRZUEUsCgkJVVBHUjFfWVIsCgkJVVBHUjFfTUlOX0hUWVBFLAoJCVVQR1IyX1lSLAoJCVVQR1IyX01JTl9UWVBFLAoJCUFUUl9TRUdNRU5UX0xFTkdUSAoJKSAlPiUKCWxlZnRfam9pbigKCQl2YW5jX3JvYWRzICU+JSBzZWxlY3QoCgkJCUlEX0RBVEFFTlRSWSwKCQkJQVRSX1NFR01FTlRfVFlQRQoJCSksCgkJYnkgPSAiSURfREFUQUVOVFJZIgoJKSAlPiUKCXJlbmFtZSgKCQlpZCA9IElEX0RBVEFFTlRSWSwKCQlpbnN0YWxsX3llYXJfb3JpZyA9IElOU1RfWVJfT1JJRywKCQlpbnN0YWxsX3llYXIgPSBJTlNUX1lSLAoJCWluc3RhbGxfdHlwZSA9IElOU1RfTUlOX0hUWVBFLAoJCXVwZ3JhZGUxX3llYXIgPSBVUEdSMV9ZUiwKCQl1cGdyYWRlMV90eXBlID0gVVBHUjFfTUlOX0hUWVBFLAoJCXVwZ3JhZGUyX3llYXIgPSBVUEdSMl9ZUiwKCQl1cGdyYWRlMl90eXBlID0gVVBHUjJfTUlOX1RZUEUsCgkJc2VnbWVudF9sZW4gPSBBVFJfU0VHTUVOVF9MRU5HVEgsCgkJc2VnbWVudF90eXBlID0gQVRSX1NFR01FTlRfVFlQRQoJKSAlPiUKCW11dGF0ZSgKCQlzZWdtZW50X2xlbiA9IHNlZ21lbnRfbGVuIC8gMTAwMCwKCQlyb2FkX3R5cGUgPSBjYXNlX3doZW4oICMgY3JlYXRlIHJvYWQgdHlwZXMKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBhcnRlcmlhbCBlcXVpdgoJICAgICAgICAJIkFydGVyaWFsIgoJICAgICAgICApIH4gIkFydGVyaWFsIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBjb2xsZWN0b3IgZXF1aXYKCSAgICAgICAgCSJDb2xsZWN0b3IiLAoJICAgICAgICAJIlNlY29uZGFyeSBBcnRlcmlhbCIsCgkgICAgICAgIAkiU2VjIEFydGVyaWFsIgoJICAgICAgICApIH4gIkNvbGxlY3RvciIsCgkgICAgICAgIHNlZ21lbnRfdHlwZSAlaW4lIGMoICMgbG9jYWwgZXF1aXYKCSAgICAgICAgCSJMYW5lIiwKCSAgICAgICAgCSJSZXNpZGVudGlhbCIsCgkgICAgICAgIAkiTGVhc2VkIiwKCSAgICAgICAgCSJSZWNyZWF0aW9uYWwiCgkgICAgICAgICkgfiAiTG9jYWwiLAoJICAgICAgICAuZGVmYXVsdCA9IHNlZ21lbnRfdHlwZQoJICAgICkKCSkKdmFuYwpgYGAKCiMjIENhbGdhcnkgUmF3IERhdGEKCmBgYHtyfQoKIyBMb2FkIHJhdyBkYXRhCmNhbGdfYmlrZXdheXMgPC0gcmVhZF9jc3YoIi4uL2RhdGEvY2FsZ2FyeV9iaWtld2F5c18yMDA5XzIwMjJfdjEuY3N2IikKY2FsZ19yb2FkcyA8LSByZWFkX2NzdigiLi4vZGF0YS9jYWxnYXJ5X3JvYWRzXzIwMDlfMjAyMl92MS5jc3YiKQoKIyBDb21iaW5lIHJhdyBkYXRhCmNhbGcgPC0gY2FsZ19iaWtld2F5cyAlPiUKICAgIHNlbGVjdCgKCQlTSEFQRV9JRCwKCQlZRUFSX09SSUcsCgkJSU5TVF9ZUiwKCQlJTlNUX01JTl9IVFlQRSwKCQlVUEdSMV9ZUiwKCQlVUEdSMV9NSU5fSFRZUEUsCgkJVVBHUjJfWVIsCgkJVVBHUjJfTUlOX0hUWVBFLAoJCUFUUl9TRUdNRU5UX0xFTkdUSAoJKSAlPiUKCWxlZnRfam9pbigKCQljYWxnX3JvYWRzICU+JSBzZWxlY3QoCgkJCXNoYXBlX2lkLAoJCQljdHBfY2xhc3MKCQkpLAoJCWJ5ID0gam9pbl9ieShTSEFQRV9JRCA9PSBzaGFwZV9pZCkKCSkgJT4lCglyZW5hbWUoCgkJaWQgPSBTSEFQRV9JRCwKCQlpbnN0YWxsX3llYXJfb3JpZyA9IFlFQVJfT1JJRywKCQlpbnN0YWxsX3llYXIgPSBJTlNUX1lSLAoJCWluc3RhbGxfdHlwZSA9IElOU1RfTUlOX0hUWVBFLAoJCXVwZ3JhZGUxX3llYXIgPSBVUEdSMV9ZUiwKCQl1cGdyYWRlMV90eXBlID0gVVBHUjFfTUlOX0hUWVBFLAoJCXVwZ3JhZGUyX3llYXIgPSBVUEdSMl9ZUiwKCQl1cGdyYWRlMl90eXBlID0gVVBHUjJfTUlOX0hUWVBFLAoJCXNlZ21lbnRfbGVuID0gQVRSX1NFR01FTlRfTEVOR1RILAoJCXNlZ21lbnRfdHlwZSA9IGN0cF9jbGFzcwoJKSAlPiUKCW11dGF0ZSgKCQlzZWdtZW50X2xlbiA9IHNlZ21lbnRfbGVuIC8gMTAwMCwKCQlyb2FkX3R5cGUgPSBjYXNlX3doZW4oICMgY3JlYXRlIHJvYWQgdHlwZXMKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBhcnRlcmlhbCBlcXVpdgoJICAgICAgICAJIkFydGVyaWFsIFN0cmVldCIsCgkgICAgICAgIAkiSW5kdXN0cmlhbCBBcnRlcmlhbCIsCgkgICAgICAgIAkiTG9jYWwgQXJ0ZXJpYWwiLAoJICAgICAgICAJIlBhcmt3YXkiLAoJICAgICAgICAJIlVyYmFuIEJvdWxldmFyZCIKCSAgICAgICAgKSB+ICJBcnRlcmlhbCIsCgkgICAgICAgIHNlZ21lbnRfdHlwZSAlaW4lIGMoICMgY29sbGVjdG9yIGVxdWl2CgkgICAgICAgIAkiTmVpZ2hib3VyaG9vZCBCb3VsZXZhcmQiLAoJICAgICAgICAJIkNvbGxlY3RvciIsCgkgICAgICAgIAkiUHJpbWFyeSBDb2xsZWN0b3IiLAoJICAgICAgICAJIlNrZWxldGFsIFJvYWQiCgkgICAgICAgICkgfiAiQ29sbGVjdG9yIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBsb2NhbCBlcXVpdgoJICAgICAgICAJIkFjY2VzcyBSb3V0ZSIsCgkgICAgICAgIAkiUmVzaWRlbnRpYWwgU3RyZWV0IiwKCSAgICAgICAgCSJBY3Rpdml0eSBDZW50ZXIgU3RyZWV0IiwKCSAgICAgICAgCSJIaXN0b3JpYyBSb2FkIEFsbG93YW5jZSIsCgkgICAgICAgIAkiTGFuZXMgKEFsbGV5cykiLAoJICAgICAgICAJIkluZHVzdHJpYWwgU3RyZWV0IgoJICAgICAgICApIH4gIkxvY2FsIiwKCSAgICAgICAgLmRlZmF1bHQgPSBzZWdtZW50X3R5cGUKCSAgICApCgkpCmNhbGcKYGBgCgojIyBUb3JvbnRvIFJhdyBEYXRhCgpgYGB7cn0KdG9yb25fcmF3IDwtIHN0X3JlYWQoIi4uL2RhdGEvcmF3L1Rvcm9udG8gQVMgRXhwb3J0L1Rvcm9udG9fQVNfMTMyMy5zaHAiKQp0b3Jvbl9yYXcKYGBgCgojIFByZXByb2Nlc3NpbmcKCiMjIFRvcm9udG8gUHJlcHJvY2Vzc2VkIERhdGEKCk5vdGUgMTogV29ya2luZyBvbiBnZXR0aW5nIGNvbXBsZXRlIGRhdGEuCgpgYGB7cn0KYGBgCgpOb3RlIDI6IFVzaW5nIHRoaXMgb25lIGZvciBub3cuCgpgYGB7cn0KCiMgTG9hZCByYXcgZGF0YQp0b3Jvbl9iaWtld2F5cyA8LSByZWFkX2NzdigiLi4vZGF0YS90b3JvbnRvX2Jpa2V3YXlzXzIwMDlfMjAyMl92MS5jc3YiKQp0b3Jvbl9yb2FkcyA8LSByZWFkX2NzdigiLi4vZGF0YS90b3JvbnRvX3JvYWRzXzIwMDlfMjAyMl92MS5jc3YiKQoKIyBDb21iaW5lIHJhdyBkYXRhCnRvcm9uIDwtIHRvcm9uX2Jpa2V3YXlzICU+JQogICAgc2VsZWN0KAoJCUlEX09JRCwKCQlJTlNUQUxMRURfT1JJRywKCQlJTlNUX1lSLAoJCUlOU1RfTUlOX0hUWVBFLAoJCVVQR1IxX1lSLAoJCVVQR1IxX01JTl9IVFlQRSwKCQlVUEdSMl9ZUiwKCQlVUEdSMl9NSU5fSFRZUEUsCgkJQVRSX1NFR01FTlRfTEVOR1RICiAgICApICU+JQoJbGVmdF9qb2luKAoJCXRvcm9uX3JvYWRzICU+JSBzZWxlY3QoCgkJCU9JRF8sCgkJCUZFQVRVUkUzNgoJCSksCgkJYnkgPSBqb2luX2J5KElEX09JRCA9PSBPSURfKQoJKSAlPiUKCXJlbmFtZSgKCQlpZCA9IElEX09JRCwKCQlpbnN0YWxsX3llYXJfb3JpZyA9IElOU1RBTExFRF9PUklHLAoJCWluc3RhbGxfeWVhciA9IElOU1RfWVIsCgkJaW5zdGFsbF90eXBlID0gSU5TVF9NSU5fSFRZUEUsCgkJdXBncmFkZTFfeWVhciA9IFVQR1IxX1lSLAoJCXVwZ3JhZGUxX3R5cGUgPSBVUEdSMV9NSU5fSFRZUEUsCgkJdXBncmFkZTJfeWVhciA9IFVQR1IyX1lSLAoJCXVwZ3JhZGUyX3R5cGUgPSBVUEdSMl9NSU5fSFRZUEUsCgkJc2VnbWVudF9sZW4gPSBBVFJfU0VHTUVOVF9MRU5HVEgsCgkJc2VnbWVudF90eXBlID0gRkVBVFVSRTM2CgkpICU+JQoJbXV0YXRlKAoJCXNlZ21lbnRfbGVuID0gc2VnbWVudF9sZW4gLyAxMDAwLAoJCXJvYWRfdHlwZSA9IGNhc2Vfd2hlbiggIyBjcmVhdGUgcm9hZCB0eXBlcwoJICAgICAgICBzZWdtZW50X3R5cGUgJWluJSBjKCAjIGFydGVyaWFsIGVxdWl2CgkgICAgICAgIAkiTWFqb3IgQXJ0ZXJpYWwiLAoJICAgICAgICAJIk1ham9yIEFydGVyaWFsIFJhbXAiLAoJICAgICAgICAJIk1pbm9yIEFydGVyaWFsIgoJICAgICAgICApIH4gIkFydGVyaWFsIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggIyBjb2xsZWN0b3IgZXF1aXYKCSAgICAgICAgCSJDb2xsZWN0b3IiCgkgICAgICAgICkgfiAiQ29sbGVjdG9yIiwKCSAgICAgICAgc2VnbWVudF90eXBlICVpbiUgYyggICMgbG9jYWwgZXF1aXYKCSAgICAgICAgCSJMb2NhbCIsCgkgICAgICAgIAkiT3RoZXIiCgkgICAgICAgICkgfiAiTG9jYWwiLAoJICAgICAgICAuZGVmYXVsdCA9IHNlZ21lbnRfdHlwZQoJICAgICkKCSkKdG9yb24KYGBgCgojIEZpZ3VyZXMKCiMjIEZpZ3VyZSAxOiBGbG93IGRpYWdyYW0gb2YgaW5jbHVzaW9uIGNyaXRlcmlhIGZvciBiaWtld2F5IHNlZ21lbnRzIGluIFZhbmNvdXZlciwgQ2FsZ2FyeSwgYW5kIFRvcm9udG8uCgpUaGlzIGZsb3djaGFydCBwcm92aWRlcyBhIGhpZ2gtbGV2ZWwgb3ZlcnZpZXcgb2YgdGhlIHNlZ21lbnQgaW5jbHVzaW9ucyBhbmQgZXhjbHVzaW9ucyBmb3IgZWFjaCBtdW5pY2lwYWxpdHkuIERhdGEgZnJvbSBDYWxnYXJ5IHdlcmUgc3BlY2lmaWMgdG8gb24tc3RyZWV0IHJvdXRlcyBvbmx5LiBGb3IgZGV0YWlsZWQgZmxvdyBkaWFncmFtcyBzcGVjaWZpYyB0byBlYWNoIG11bmljaXBhbGl0eSwgcGxlYXNlIHJlZmVyIHRvIHRoZSBBcHBlbmRpeC4KCmBgYHtyfQpnclZpeigiCmRpZ3JhcGggewoJcmFua2RpciA9IExSCglub2RlWwoJCXNoYXBlID0gYm94LAoJCXdpZHRoID0gMi43NSwKCQloZWlnaHQgPSAxLjM1LAoJCXN0eWxlID0gZmlsbGVkLAoJCWZpbGxjb2xvciA9IHdoaXRlLAoJCXBlbndpZHRoID0gMS41LAoJCWZvbnRuYW1lID0gJ0FyaWFsJwoJXQoJbGF5b3V0ID0gbmVhdG8KCQoJZmlsdGVyMVsKCQlsYWJlbCA9ICdGaWx0ZXJpbmcnLAoJCWhlaWdodCA9IDAuMjUsCgkJc2hhcGUgPSBwbGFpbnRleHQsCgkJc3R5bGU9JycsIHBvcyA9ICcxLjYsMS40MjUhJwoJXQoJZmlsdGVyMlsKCQlzdHlsZSA9IGludmlzLAoJCXBvcyA9ICcxLjYsLTMuOSEnCgldCglmaWx0ZXIxIC0+IGZpbHRlcjIgW3N0eWxlID0gZGFzaGVkLCBkaXIgPSBub25lLCBjb2xvciA9ICcjYjBiMGIwJ10KCQoJc2NyZWVuMVsKCQlsYWJlbCA9ICdTY3JlZW5pbmcnLAoJCWhlaWdodCA9IDAuMjUsCgkJc2hhcGUgPSBwbGFpbnRleHQsCgkJc3R5bGU9JycsIHBvcyA9ICc0Ljg1LDEuNDI1IScKCV0KCXNjcmVlbjJbCgkJc3R5bGUgPSBpbnZpcywKCQlwb3MgPSAnNC44NSwtMy45IScKCV0KCXNjcmVlbjEgLT4gc2NyZWVuMiBbc3R5bGUgPSBkYXNoZWQsIGRpciA9IG5vbmUsIGNvbG9yID0gJyNiMGIwYjAnXQoJCglvcGVuX2RhdGFbCgkJbGFiZWwgPSAnT3BlbiBEYXRhJywKCQloZWlnaHQgPSAwLjUsCgkJZmlsbGNvbG9yID0gJyNkN2U5ZmUnLAoJCXBvcyA9ICcwLDEhJwoJXQoJZWxpZ19kYXRhWwoJCWxhYmVsID0gJ0VsaWdpYmxlIFNlZ21lbnRzJywKCQloZWlnaHQgPSAwLjUsCgkJZmlsbGNvbG9yID0gJyNkN2U5ZmUnLAoJCXBvcyA9ICczLjI1LDEhJwoJXQoJaW5jbF9kYXRhWwoJCWxhYmVsID0gJ0luY2x1c2lvbnMnLAoJCWhlaWdodCA9IDAuNSwKCQlmaWxsY29sb3IgPSAnI2Q3ZTlmZScsCgkJcG9zID0gJzYuNSwxIScKCV0KCQoJb3Blbl92YW5jWwoJCWxhYmVsID0gPDxiPlZhbmNvdXZlcjwvYj48YnIvPk4gPSBAQDEtMSBTZWdtZW50czxici8+PGk+KEBAMi0xKTxici8+RG93bmxvYWRlZDogSmFudWFyeSAyMDIzPC9pPj4sCgkJcG9zID0gJzAsLTAuMDYhJwoJXQoJb3Blbl9jYWxnWwoJCWxhYmVsID0gPDxiPkNhbGdhcnk8L2I+PGJyLz5OID0gQEAxLTIgU2VnbWVudHM8YnIvPjxpPihAQDItMik8YnIvPkRvd25sb2FkZWQ6IEphbnVhcnkgMjAyMzwvaT4+LAoJCXBvcyA9ICcwLC0xLjY1IScKCV0KCW9wZW5fdG9yb25bCgkJbGFiZWwgPSA8PGI+VG9yb250bzwvYj48YnIvPk4gPSBAQDEtMyBTZWdtZW50czxici8+PGk+KEBAMi0zKTxici8+RG93bmxvYWRlZDogSmFudWFyeSAyMDIzPC9pPj4sIAoJCXBvcyA9ICcwLC0zLjI0IScKCV0KCQoJZWxpZ192YW5jWwoJCWxhYmVsID0gPG4gPSBAQDMtMSBTZWdtZW50czxici8+KEBANC0xKTxici8+PGk+PGI+RXhjbHVzaW9uczwvYj5AQDUtMUBANi0xQEA3LTE8L2k+PiwKCQlwb3MgPSAnMy4yNSwtMC4wNiEnCgldCgkKCWVsaWdfY2FsZ1sKCQlsYWJlbCA9IDxuID0gQEAzLTIgU2VnbWVudHM8YnIvPjxpPihAQDQtMik8YnIvPjxiPkV4Y2x1c2lvbnM8L2I+QEA1LTJAQDYtMkBANy0yPC9pPj4sCgkJcG9zID0gJzMuMjUsLTEuNjUhJwoJXQoJZWxpZ190b3JvblsKCQlsYWJlbCA9IDxuID0gQEAzLTMgU2VnbWVudHM8YnIvPjxpPihAQDQtMyk8YnIvPjxiPkV4Y2x1c2lvbnM8L2I+QEA1LTNAQDYtM0BANy0zPC9pPj4sCgkJcG9zID0gJzMuMjUsLTMuMjQhJwoJXQoJCglpbmNsX3ZhbmNbCgkJbGFiZWwgPSA8biA9IEBAOC0xIFNlZ21lbnRzPGJyLz48aT4oQEA5LTEpPGJyLz48Yj5FeGNsdXNpb25zPC9iPkBAMTAtMUBAMTEtMTwvaT4+LAoJCXBvcyA9ICc2LjUsLTAuMDYhJwoJXQoJaW5jbF9jYWxnWwoJCWxhYmVsID0gPG4gPSBAQDgtMiBTZWdtZW50czxici8+PGk+KEBAOS0yKTxici8+PGI+RXhjbHVzaW9uczwvYj5AQDEwLTJAQDExLTI8L2k+PiwKCQlwb3MgPSAnNi41LC0xLjY1IScKCV0KCWluY2xfdG9yb25bCgkJbGFiZWwgPSA8biA9IEBAOC0zIFNlZ21lbnRzPGJyLz48aT4oQEA5LTMpPGJyLz48Yj5FeGNsdXNpb25zPC9iPkBAMTAtM0BAMTEtMzwvaT4+LAoJCXBvcyA9ICc2LjUsLTMuMjQhJwoJXQoJCglub3RlWwoJCWxhYmVsPTw8aT5AQDEyPC9pPj4sCgkJc3R5bGUgPSAnJywKCQlzaGFwZSA9IHBsYWludGV4dCwKCQlmb250c2l6ZSA9IDEyLAoJCXBvcyA9ICczLjI1LC00LjE1IScKCV0KCQoJb3Blbl92YW5jIC0+IGVsaWdfdmFuYyAtPiBpbmNsX3ZhbmMKCW9wZW5fY2FsZyAtPiBlbGlnX2NhbGcgLT4gaW5jbF9jYWxnCglvcGVuX3Rvcm9uIC0+IGVsaWdfdG9yb24gLT4gaW5jbF90b3Jvbgp9CgpbMV06IGMoJzM2NjQnLCAnNDE2NicsICcxMzIzJykgIyBvcGVuIHNlZ21lbnRzClsyXTogYygnMzQyLjEga20nLCAnNTY5Ljcga20nLCAnNzU1Ljgga20nKSAjIG9wZW4ga20KWzNdOiBjKCc3ODAnLCAnNzgyJywgJzMzMScpICMgZWxpZ2libGUgc2VnbWVudHMKWzRdOiBjKCc3MS4yIGttJywgJzg4Ljcga20nLCAnMjA1LjQga20nKSAjIGVsaWdpYmxlIGttCls1XTogYygnPGJyLz5JbmVsaWdpYmxlOiBuPTI4ODMnLCAnPGJyLz5JbmVsaWdpYmxlOiBuPTMzODMnLCAnPGJyLz5JbmVsaWdpYmxlOiBuPTk5MicpICMgZWxpZ2libGUgZXhjbHVzaW9ucwpbNl06IGMoJzxici8+RHVwbGljYXRlcyBuPTEnLCAnJywgJycpICMgZWxpZ2JsZSBkdXBsaWNhdGVzCls3XTogYygnJywgJzxici8+Tm8gUG9seWxpbmUgRGF0YTogbj0xJywgJycpICMgZWxpZ2libGUgcG9seWxpbmUgZGF0YQpbOF06IGMoJzc0NScsICc3NTAnLCAnMzI2JykgIyBpbmNsdXNpb24gc2VnbWVudHMKWzldOiBjKCc2OS45IGttJywgJzg1IGttJywgJzIwMy41IGttJykgIyBpbmNsdXNpb24ga20KWzEwXTogYygnPGJyLz4qTWlzY2xhc3NpZmljYXRpb25zOiBuPTM0JywgJzxici8+Kk1pc2NsYXNzaWZpY2F0aW9uczogbj0zMicsICc8YnIvPipNaXNjbGFzc2lmaWNhdGlvbnM6IG49NScpICMgaW5jbHVzaW9uIG1pc2NsYXNzaWZpY2F0aW9ucwpbMTFdOiBjKCc8YnIvPkR1cGxpY2F0ZXM6IG49MScsICcnLCAnJykgIyBpbmNsdXNpb24gZHVwbGljYXRlcwpbMTJdOiAnKkRlbm90ZXMgc2VnbWVudHMgbWlzY2xhc3NpZmllZCBhcyBhbiBpbmVsaWdpYmxlIHR5cGUgKG9mZi1zdHJlZXQgcGF0aCwgc2hhcmVkIHJvYWQsIG9yIGluYWN0aXZlIHRlbXBvcmFyeSBpbmZyYXN0cnVjdHVyZSknCiIpCmBgYAoKYGBgICAgICAgICAgCiU+JQogICAgZXhwb3J0X3N2ZyAlPiUKICAgIGNoYXJUb1JhdyAlPiUKICAgIHJzdmdfcG5nKCJ0ZXN0LnBuZyIpCmBgYAoKIyMgRmlndXJlIDI6IENoYW5nZXMgaW4gZGVkaWNhdGVkIGN5Y2xpbmcgaW5mcmFzdHJ1Y3R1cmUgYmV0d2VlbiAyMDA5IGFuZCAyMDIyIGZvciBWYW5jb3V2ZXIsIENhbGdhcnksIGFuZCBUb3JvbnRvIGJ5IGluZnJhc3RydWN0dXJlIGNhdGVnb3J5LgoKQXNzZXNzZWQgdXNpbmcgcm9hZHdheSBjZW50cmVsaW5lLWttLCB3aXRoIGluZnJhc3RydWN0dXJlIGNsYXNzaWZpY2F0aW9ucyBkZXRlcm1pbmVkIGJ5IHRoZSBtb3N0IHByb3RlY3RpdmUgZWxlbWVudCBwcmVzZW50IGFsb25nIGVhY2ggcm9hZCBzZWdtZW50LgoKYGBge3IgZmlnLndpZHRoPTguNSwgZmlnLmhlaWdodD0xMX0KcGxvdF95ZWFybHlfbGVuX2luZnJhKGxpc3QoCgkiVmFuY291dmVyLCBDQSIgPSB2YW5jLAoJIkNhbGdhcnksIENBIiA9IGNhbGcsCgkiVG9yb250bywgQ0EiID0gdG9yb24KKSkKYGBgCgojIyBGaWd1cmUgNDogQ2hhbmdlcyBpbiBEZWRpY2F0ZWQgT24tU3RyZWV0IEluZnJhc3RydWN0dXJlIFNpbmNlIEphbnVhcnkgMjAyMCBmb3IgVmFuY291dmVyLCBDYWxnYXJ5LCBhbmQgVG9yb250by4KCk5ldyBpbnN0YWxsYXRpb25zIG9mIGRlZGljYXRlZCBpbmZyYXN0cnVjdHVyZSBhcmUgZGVub3RlZCBpbiBncmVlbiwgdXBncmFkZXMgZnJvbSBhIHByZXZpb3VzIGRlZGljYXRlZCBpbmZyYXN0cnVjdHVyZSB0eXBlIGFyZSBkZW5vdGVkIGluIG9yYW5nZS4gTWFwcGVkIGluIEFyY0dJUyBQcm8gMy4wLjEuCgpgYGB7cn0KYGBgCgojIEFwcGVuZGl4IDEgLSBTdXBwbGVtZW50YXJ5IFJlc3VsdHMKCiMjIFN1cHBsZW1lbnRhcnkgRmlndXJlIDQ6IENoYW5nZXMgaW4gZGVkaWNhdGVkIGN5Y2xpbmcgaW5mcmFzdHJ1Y3R1cmUgYmV0d2VlbiAyMDA5IGFuZCAyMDIxIGZvciB0aGUgTXVuaWNpcGFsaXR5IG9mIFZhbmNvdXZlciwgQ0EuCgpCeSAoQSkgcm9hZHdheSBjbGFzc2lmaWNhdGlvbiwgYW5kIChCKSBpbmZyYXN0cnVjdHVyZSBkaXN0cmlidXRpb24gd2l0aGluIGVhY2ggcm9hZCBjbGFzcy4gQXNzZXNzZWQgdXNpbmcgcm9hZHdheSBjZW50cmVsaW5lLWttLCB3aXRoIGluZnJhc3RydWN0dXJlIGNsYXNzaWZpY2F0aW9uIGRldGVybWluZWQgYnkgdGhlIG1vc3QgcHJvdGVjdGl2ZSBlbGVtZW50IHByZXNlbnQgYWxvbmcgZWFjaCByb2FkIHNlZ21lbnQuCgpgYGB7ciBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9CnBsb3RfeWVhcmx5X2xlbl9yb2FkKAoJdmFuYywKCXRpdGxlID0gIlJvYWR3YXlzIHdpdGggRGVkaWNhdGVkIEN5Y2xpbmcgSW5mcmFzdHJ1Y3R1cmUgKFZhbmNvdXZlciwgQ0EpIgopCmBgYAoKIyMgU3VwcGxlbWVudGFyeSBGaWd1cmUgNTogQ2hhbmdlcyBpbiBkZWRpY2F0ZWQgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBiZXR3ZWVuIDIwMDkgYW5kIDIwMjIgZm9yIHRoZSBNdW5pY2lwYWxpdHkgb2YgQ2FsZ2FyeSwgQ0EuCgpCeSAoQSkgcm9hZHdheSBjbGFzc2lmaWNhdGlvbiwgYW5kIChCKSBpbmZyYXN0cnVjdHVyZSBkaXN0cmlidXRpb24gd2l0aGluIGVhY2ggcm9hZCBjbGFzcy4gQXNzZXNzZWQgdXNpbmcgcm9hZHdheSBjZW50cmVsaW5lLWttLCB3aXRoIGluZnJhc3RydWN0dXJlIGNsYXNzaWZpY2F0aW9uIGRldGVybWluZWQgYnkgdGhlIG1vc3QgcHJvdGVjdGl2ZSBlbGVtZW50IHByZXNlbnQgYWxvbmcgZWFjaCByb2FkIHNlZ21lbnQuCgpgYGB7ciBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTB9CnBsb3RfeWVhcmx5X2xlbl9yb2FkKAoJY2FsZywKCXRpdGxlID0gIlJvYWR3YXlzIHdpdGggRGVkaWNhdGVkIEN5Y2xpbmcgSW5mcmFzdHJ1Y3R1cmUgKENhbGdhcnksIENBKSIKKQpgYGAKCiMjIFN1cHBsZW1lbnRhcnkgRmlndXJlIDY6IENoYW5nZXMgaW4gZGVkaWNhdGVkIGN5Y2xpbmcgaW5mcmFzdHJ1Y3R1cmUgYmV0d2VlbiAyMDA5IGFuZCAyMDIyIGZvciB0aGUgTXVuaWNpcGFsaXR5IG9mIFRvcm9udG8sIENBLgoKQnkgKEEpIHJvYWR3YXkgY2xhc3NpZmljYXRpb24sIGFuZCAoQikgaW5mcmFzdHJ1Y3R1cmUgZGlzdHJpYnV0aW9uIHdpdGhpbiBlYWNoIHJvYWQgY2xhc3MuIEFzc2Vzc2VkIHVzaW5nIHJvYWR3YXkgY2VudHJlbGluZS1rbSwgd2l0aCBpbmZyYXN0cnVjdHVyZSBjbGFzc2lmaWNhdGlvbiBkZXRlcm1pbmVkIGJ5IHRoZSBtb3N0IHByb3RlY3RpdmUgZWxlbWVudCBwcmVzZW50IGFsb25nIGVhY2ggcm9hZCBzZWdtZW50LgoKYGBge3IgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTEwfQpwbG90X3llYXJseV9sZW5fcm9hZCgKCXRvcm9uLAoJdGl0bGUgPSAiUm9hZHdheXMgd2l0aCBEZWRpY2F0ZWQgQ3ljbGluZyBJbmZyYXN0cnVjdHVyZSAoVG9yb250bywgQ0EpIgopCmBgYAoKIyMgU3VwcGxlbWVudGFyeSBGaWd1cmUgNzogQSBjb21wYXJhdGl2ZSBhbmFseXNpcyBiZXR3ZWVuIG11bmljaXBhbCBkYXRhIGFuZCB2ZXJpZmllZCBkYXRhIG9uIHRoZSBpbnN0YWxsYXRpb24geWVhcnMgZm9yIGN5Y2xpbmcgaW5mcmFzdHJ1Y3R1cmUgaW4gVmFuY291dmVyLCBDQS4KCkFueSBkYXRhIHdoZXJlIGEgY2l0eSBwcm92aWRlZCBpbnN0YWxsYXRpb24geWVhciB3YXMgbWlzc2luZyBvciB0aGUgdmVyaWZpZWQgeWVhciBvY2N1cnJlZCBlYXJsaWVyIHRoYW4gdGhlIHN0YXJ0IG9mIHRoZSBzdHVkeSBwZXJpb2QgKDIwMDkpIGhhcyBiZWVuIGV4Y2x1ZGVkIGZyb20gYW5hbHlzaXMsIHlpZWxkaW5nIG4gPSAyNTIgc2VnbWVudHMuIFRoZSBncmFwaCBzaG93cyB0aGF0IDgzLjMlIG9mIHRoZSBpbmNsdWRlZCBzZWdtZW50cyBoYWQgdGhlIGNvcnJlY3QgaW5zdGFsbGF0aW9uIHllYXIgYXMgcGVyIHRoZSBjaXR5J3MgZGF0YSwgYW5kIDk3LjYlIHdlcmUgYWNjdXJhdGUgd2l0aGluIGEgcmFuZ2Ugb2YgwrExIHllYXIuCgpgYGB7ciBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF95ZWFybHlfZGlmZigKCXZhbmMsCgl0aXRsZSA9ICJEaWZmZXJlbmNlIGluIEluc3RhbGxhdGlvbiBZZWFycywgQ29tcGFyaW5nIENpdHkgRGF0YSBhbmQgVmVyaWZpZWQgRGF0YTogVmFuY291dmVyLCBDQSIKKQpgYGAKCiMjIFN1cHBsZW1lbnRhcnkgRmlndXJlIDg6IEEgY29tcGFyYXRpdmUgYW5hbHlzaXMgYmV0d2VlbiBtdW5pY2lwYWwgZGF0YSBhbmQgdmVyaWZpZWQgZGF0YSBvbiB0aGUgaW5zdGFsbGF0aW9uIHllYXJzIGZvciBjeWNsaW5nIGluZnJhc3RydWN0dXJlIGluIENhbGdhcnksIENBLgoKQW55IGRhdGEgd2hlcmUgYSBjaXR5IHByb3ZpZGVkIGluc3RhbGxhdGlvbiB5ZWFyIHdhcyBtaXNzaW5nIG9yIHRoZSB2ZXJpZmllZCB5ZWFyIG9jY3VycmVkIGVhcmxpZXIgdGhhbiB0aGUgc3RhcnQgb2YgdGhlIHN0dWR5IHBlcmlvZCAoMjAwOSkgaGFzIGJlZW4gZXhjbHVkZWQgZnJvbSBhbmFseXNpcywgeWllbGRpbmcgbj02NzAgc2VnbWVudHMuIFRoZSBncmFwaCBzaG93cyB0aGF0IDQyLjElIG9mIHRoZSBpbmNsdWRlZCBzZWdtZW50cyBoYWQgdGhlIGNvcnJlY3QgaW5zdGFsbGF0aW9uIHllYXIgYXMgcGVyIHRoZSBjaXR5J3MgZGF0YSwgYW5kIDYyLjglIHdlcmUgYWNjdXJhdGUgd2l0aGluIGEgcmFuZ2Ugb2YgwrExIHllYXIuCgpgYGB7ciBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF95ZWFybHlfZGlmZigKCWNhbGcsCgl0aXRsZSA9ICJEaWZmZXJlbmNlIGluIEluc3RhbGxhdGlvbiBZZWFycywgQ29tcGFyaW5nIENpdHkgRGF0YSBhbmQgVmVyaWZpZWQgRGF0YTogQ2FsZ2FyeSwgQ0EiCikKYGBgCgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSA5OiBBIGNvbXBhcmF0aXZlIGFuYWx5c2lzIGJldHdlZW4gbXVuaWNpcGFsIGRhdGEgYW5kIHZlcmlmaWVkIGRhdGEgb24gdGhlIGluc3RhbGxhdGlvbiB5ZWFycyBmb3IgY3ljbGluZyBpbmZyYXN0cnVjdHVyZSBpbiBUb3JvbnRvLCBDQS4KCkFueSBkYXRhIHdoZXJlIGEgY2l0eSBwcm92aWRlZCBpbnN0YWxsYXRpb24geWVhciB3YXMgbWlzc2luZyBvciB0aGUgdmVyaWZpZWQgeWVhciBvY2N1cnJlZCB0aGFuIHRoZSBzdGFydCBvZiB0aGUgc3R1ZHkgcGVyaW9kICgyMDA5KSBoYXMgYmVlbiBleGNsdWRlZCBmcm9tIGFuYWx5c2lzLCB5aWVsZGluZyBuPTE5MiBzZWdtZW50cy4gVGhlIGdyYXBoIHNob3dzIHRoYXQgNzUuNSUgb2YgdGhlIGluY2x1ZGVkIHNlZ21lbnRzIGhhZCB0aGUgY29ycmVjdCBpbnN0YWxsYXRpb24geWVhciBhcyBwZXIgdGhlIGNpdHkncyBkYXRhLCBhbmQgNzguMSUgd2VyZSBhY2N1cmF0ZSB3aXRoaW4gYSByYW5nZSBvZiDCsTEgeWVhci4KCmBgYHtyIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwbG90X3llYXJseV9kaWZmKAoJdG9yb24sCgl0aXRsZSA9ICJEaWZmZXJlbmNlIGluIEluc3RhbGxhdGlvbiBZZWFycywgQ29tcGFyaW5nIENpdHkgRGF0YSBhbmQgVmVyaWZpZWQgRGF0YTogVG9yb250bywgQ0EiCikKYGBgCgojIENvbnRyaWJ1dGlvbnMKClJpY2hhcmQgV2VuIGRldmVsb3BlZCByZXByb2R1Y2libGUgUiBjb2RlIGFuZCBvcmdhbml6ZWQgdGhlIGRhdGEgYmFzZWQgb24gS29ucmFkIFNhbXNlbCdzIGRyYWZ0IG1hbnVzY3JpcHQgYW5kIHByZXZpb3VzIFIgY29kZS4gS29ucmFkIFNhbXNlbCBwcmVwYXJlZCBkcmFmdCBtYW51c2NyaXB0LCByYXcgZGF0YSwgYW5kIHByb3ZpZGVkIGNvbnN1bHRhdGlvbiBvbiBkYXRhIGFuZCBtZXRob2RzLgoKIyBBY2tub3dsZWRnZW1lbnRzCgpMaW5kYSBSb3RobWFuIGFuZCBCcmljZSBCYXRvbWVuIHByb3ZpZGVkIHN1cGVydmlzaW9uLCBwcm9qZWN0IGFkbWluaXN0cmF0aW9uLCByZXNvdXJjZXMsIGZ1bmRpbmcsIGFuZCByZXZpZXcvZWRpdGluZyBmb3IgdGhlIGRyYWZ0IG1hbnVzY3JpcHQuCg==